成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

按下鍵盤后為什么屏幕上就會(huì)有輸出

網(wǎng)絡(luò) 通信技術(shù)
內(nèi)存中有這樣一部分區(qū)域,是和顯存映射的。啥意思,就是你往上圖的這些內(nèi)存區(qū)域中寫數(shù)據(jù),相當(dāng)于寫在了顯存中。而往顯存中寫數(shù)據(jù),就相當(dāng)于在屏幕上輸出文本了。

書接上回,上回書咱們說到,繼內(nèi)存管理結(jié)構(gòu) mem_map 和中斷描述符表 idt 建立好之后,我們又在內(nèi)存中倒騰出一個(gè)新的數(shù)據(jù)結(jié)構(gòu) request。

并且把它們都放在了一個(gè)數(shù)組中。

這是塊設(shè)備驅(qū)動(dòng)程序與內(nèi)存緩沖區(qū)的橋梁,通過它可以完整地表示一個(gè)塊設(shè)備讀寫操作要做的事。

我們繼續(xù)往下看,tty_init。

  1. void main(void) { 
  2.     ... 
  3.     mem_init(main_memory_start,memory_end); 
  4.     trap_init(); 
  5.     blk_dev_init(); 
  6.     chr_dev_init(); 
  7.     tty_init(); 
  8.     time_init(); 
  9.     sched_init(); 
  10.     buffer_init(buffer_memory_end); 
  11.     hd_init(); 
  12.     floppy_init(); 
  13.      
  14.     sti(); 
  15.     move_to_user_mode(); 
  16.     if (!fork()) {init();} 
  17.     for(;;) pause(); 

這個(gè)方法執(zhí)行完成之后,我們將會(huì)具備鍵盤輸入到顯示器輸出字符這個(gè)最常用的功能。

打開這個(gè)函數(shù)后我有點(diǎn)慌。

  1. void tty_init(void) 
  2.     rs_init(); 
  3.     con_init(); 

看來這個(gè)方法已經(jīng)多到需要拆成兩個(gè)子方法了。

打開第一個(gè)方法,還好。

  1. void rs_init(void) 
  2.     set_intr_gate(0x24,rs1_interrupt); 
  3.     set_intr_gate(0x23,rs2_interrupt); 
  4.     init(tty_table[1].read_q.data); 
  5.     init(tty_table[2].read_q.data); 
  6.     outb(inb_p(0x21)&0xE7,0x21); 

這個(gè)方法是串口中斷的開啟,以及設(shè)置對(duì)應(yīng)的中斷處理程序,串口在我們現(xiàn)在的 PC 機(jī)上已經(jīng)很少用到了,所以這個(gè)直接忽略,要講我也不懂。

看第二個(gè)方法,這是重點(diǎn)。代碼非常長(zhǎng),有點(diǎn)嚇人,我先把大體框架寫出。

  1. void con_init(void) { 
  2.     ... 
  3.     if (ORIG_VIDEO_MODE == 7) { 
  4.         ... 
  5.         if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) {...} 
  6.         else {...} 
  7.     } else { 
  8.         ... 
  9.         if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) {...} 
  10.         else {...} 
  11.     } 
  12.     ... 

可以看出,非常多的 if else。

這是為了應(yīng)對(duì)不同的顯示模式,來分配不同的變量值,那如果我們僅僅找出一個(gè)顯示模式,這些分支就可以只看一個(gè)了。 啥是顯示模式呢?那我們得簡(jiǎn)單說說顯示,一個(gè)字符是如何顯示在屏幕上的呢?換句話說,如果你可以隨意操作內(nèi)存和 CPU 等設(shè)備,你如何操作才能使得你的顯示器上,顯示一個(gè)字符‘a’呢?

我們先看一張圖。

內(nèi)存中有這樣一部分區(qū)域,是和顯存映射的。啥意思,就是你往上圖的這些內(nèi)存區(qū)域中寫數(shù)據(jù),相當(dāng)于寫在了顯存中。而往顯存中寫數(shù)據(jù),就相當(dāng)于在屏幕上輸出文本了。

沒錯(cuò),就是這么簡(jiǎn)單。 如果我們寫這一行匯編語(yǔ)句。

  1. mov [0xB8000],'h' 

后面那個(gè) h 相當(dāng)于匯編編輯器幫我們轉(zhuǎn)換成 ASCII 碼的二進(jìn)制數(shù)值,當(dāng)然我們也可以直接寫。

  1. mov [0xB8000],0x68 

其實(shí)就是往內(nèi)存中 0xB8000 這個(gè)位置寫了一個(gè)值,只要一寫,屏幕上就會(huì)是這樣。

簡(jiǎn)單吧,具體說來,這片內(nèi)存是每?jī)蓚€(gè)字節(jié)表示一個(gè)顯示在屏幕上的字符,第一個(gè)是字符的編碼,第二個(gè)是字符的顏色,那我們先不管顏色,如果多寫幾個(gè)字符就像這樣。

  1. mov [0xB8000],'h' 
  2. mov [0xB8002],'e' 
  3. mov [0xB8004],'l' 
  4. mov [0xB8006],'l' 
  5. mov [0xB8008],'o' 

此時(shí)屏幕上就會(huì)是這樣。

是不是賊簡(jiǎn)單?那我們回過頭看剛剛的代碼,我們就假設(shè)顯示模式是我們現(xiàn)在的這種文本模式,那條件分支就可以去掉好多。 代碼可以簡(jiǎn)化成這個(gè)樣子。

  1. #define ORIG_X          (*(unsigned char *)0x90000) 
  2. #define ORIG_Y          (*(unsigned char *)0x90001) 
  3. void con_init(void) { 
  4.     register unsigned char a; 
  5.     // 第一部分 獲取顯示模式相關(guān)信息 
  6.     video_num_columns = (((*(unsigned short *)0x90006) & 0xff00) >> 8); 
  7.     video_size_row = video_num_columns * 2; 
  8.     video_num_lines = 25; 
  9.     video_page = (*(unsigned short *)0x90004); 
  10.     video_erase_char = 0x0720; 
  11.     // 第二部分 顯存映射的內(nèi)存區(qū)域  
  12.     video_mem_start = 0xb8000; 
  13.     video_port_reg  = 0x3d4; 
  14.     video_port_val  = 0x3d5; 
  15.     video_mem_end = 0xba000; 
  16.     // 第三部分 滾動(dòng)屏幕操作時(shí)的信息 
  17.     origin  = video_mem_start; 
  18.     scr_end = video_mem_start + video_num_lines * video_size_row; 
  19.     top = 0; 
  20.     bottom  = video_num_lines; 
  21.     // 第四部分 定位光標(biāo)并開啟鍵盤中斷 
  22.     gotoxy(ORIG_X, ORIG_Y); 
  23.     set_trap_gate(0x21,&keyboard_interrupt); 
  24.     outb_p(inb_p(0x21)&0xfd,0x21); 
  25.     a=inb_p(0x61); 
  26.     outb_p(a|0x80,0x61); 
  27.     outb(a,0x61); 

別看這么多,一點(diǎn)都不難。

首先還記不記得之前匯編語(yǔ)言的時(shí)候做的工作,存了好多以后要用的數(shù)據(jù)在內(nèi)存中。

內(nèi)存地址 長(zhǎng)度(字節(jié)) 名稱
0x90000 2 光標(biāo)位置
0x90002 2
擴(kuò)展內(nèi)存數(shù)
0x90004 2 顯示頁(yè)面
0x90006 1
顯示模式
0x90007 1 字符列數(shù)
0x90008 2 未知
0x9000A 1
顯示內(nèi)存
0x9000B 1
顯示狀態(tài)
0x9000C 2 顯卡特性參數(shù)
0x9000E 1
屏幕行數(shù)
0x9000F 1 屏幕列數(shù)
0x90080 16
硬盤1參數(shù)表
0x90090 16 硬盤2參數(shù)表
0x901FC 2
根設(shè)備號(hào)

所以,第一部分獲取 0x90006 地址處的數(shù)據(jù),就是獲取顯示模式等相關(guān)信息。

第二部分就是顯存映射的內(nèi)存地址范圍,我們現(xiàn)在假設(shè)是 CGA 類型的文本模式,所以映射的內(nèi)存是從 0xB8000 到 0xBA000。

第三部分是設(shè)置一些滾動(dòng)屏幕時(shí)需要的參數(shù),定義頂行和底行是哪里,這里頂行就是第一行,底行就是最后一行,很合理。

第四部分是把光標(biāo)定位到之前保存的光標(biāo)位置處(取內(nèi)存地址 0x90000 處的數(shù)據(jù)),然后設(shè)置并開啟鍵盤中斷。

開啟鍵盤中斷后,鍵盤上敲擊一個(gè)按鍵后就會(huì)觸發(fā)中斷,中斷程序就會(huì)讀鍵盤碼轉(zhuǎn)換成 ASCII 碼,然后寫到光標(biāo)處的內(nèi)存地址,也就相當(dāng)于往顯存寫,于是這個(gè)鍵盤敲擊的字符就顯示在了屏幕上。

這一切具體是怎么做到的呢?我們先看看我們干了什么。

1. 我們現(xiàn)在根據(jù)已有信息已經(jīng)可以實(shí)現(xiàn)往屏幕上的任意位置寫字符了,而且還能指定顏色。

2. 并且,我們也能接受鍵盤中斷,根據(jù)鍵盤碼中斷處理程序就可以得知哪個(gè)鍵按下了。

有了這倆功能,那我們想干嘛還不是為所欲為?

好,接下來我們看看代碼是怎么處理的,很簡(jiǎn)單。一切的起點(diǎn),就是第四步的 gotoxy 函數(shù),定位當(dāng)前光標(biāo)。

  1. #define ORIG_X          (*(unsigned char *)0x90000) 
  2. #define ORIG_Y          (*(unsigned char *)0x90001) 
  3. void con_init(void) { 
  4.     ... 
  5.     // 第四部分 定位光標(biāo)并開啟鍵盤中斷 
  6.     gotoxy(ORIG_X, ORIG_Y); 
  7.     ... 

這里面干嘛了呢?

  1. static inline void gotoxy(unsigned int new_x,unsigned int new_y) { 
  2.    ... 
  3.    x = new_x; 
  4.    y = new_y; 
  5.    pos = origin + y*video_size_row + (x<<1); 

就是給 x y pos 這三個(gè)參數(shù)附上了值。

其中 x 表示光標(biāo)在哪一列,y 表示光標(biāo)在哪一行,pos 表示根據(jù)列號(hào)和行號(hào)計(jì)算出來的內(nèi)存指針,也就是往這個(gè) pos 指向的地址處寫數(shù)據(jù),就相當(dāng)于往控制臺(tái)的 x 列 y 行處寫入字符了,簡(jiǎn)單吧?

然后,當(dāng)你按下鍵盤后,觸發(fā)鍵盤中斷,之后的程序調(diào)用鏈?zhǔn)沁@樣的。

  1. _keyboard_interrupt: 
  2.     ... 
  3.     call _do_tty_interrupt 
  4.     ... 
  5.      
  6. void do_tty_interrupt(int tty) { 
  7.    copy_to_cooked(tty_table+tty); 
  8.  
  9. void copy_to_cooked(struct tty_struct * tty) { 
  10.     ... 
  11.     tty->write(tty); 
  12.     ... 
  13.  
  14. // 控制臺(tái)時(shí) tty 的 write 為 con_write 函數(shù) 
  15. void con_write(struct tty_struct * tty) { 
  16.     ... 
  17.     __asm__("movb _attr,%%ah\n\t" 
  18.       "movw %%ax,%1\n\t" 
  19.       ::"a" (c),"m" (*(short *)pos) 
  20.       :"ax"); 
  21.      pos += 2; 
  22.      x++; 
  23.     ... 

前面的過程不用管,我們看最后一個(gè)函數(shù) con_write 中的關(guān)鍵代碼。

__asm__ 內(nèi)聯(lián)匯編,就是把鍵盤輸入的字符 c 寫入pos 指針指向的內(nèi)存,相當(dāng)于往屏幕輸出了。

之后兩行 pos+=2 和 x++,就是調(diào)整所謂的光標(biāo)。

你看,寫入一個(gè)字符,最底層,其實(shí)就是往內(nèi)存的某處寫個(gè)數(shù)據(jù),然后順便調(diào)整一下光標(biāo)。

由此我們也可以看出,光標(biāo)的本質(zhì),其實(shí)就是這里的 x y pos 這仨變量而已。

我們還可以做換行效果,當(dāng)發(fā)現(xiàn)光標(biāo)位置處于某一行的結(jié)尾時(shí)(這個(gè)應(yīng)該很好算吧,我們都知道屏幕上一共有幾行幾列了),就把光標(biāo)計(jì)算出一個(gè)新值,讓其處于下一行的開頭。

就一個(gè)小計(jì)算公式即可搞定,仍然在 con_write 源碼處有體現(xiàn),就是判斷列號(hào) x 是否大于了總列數(shù)。

  1. void con_write(struct tty_struct * tty) { 
  2.     ... 
  3.     if (x>=video_num_columns) { 
  4.         x -= video_num_columns; 
  5.         pos -= video_size_row; 
  6.         lf(); 
  7.   } 
  8.   ... 
  9.  
  10. static void lf(void) { 
  11.    if (y+1<bottom) { 
  12.       y++; 
  13.       pos += video_size_row; 
  14.       return
  15.    } 
  16.  ... 

相似的,我們還可以實(shí)現(xiàn)滾屏的效果,無非就是當(dāng)檢測(cè)到光標(biāo)已經(jīng)出現(xiàn)在最后一行最后一列了,那就把每一行的字符,都復(fù)制到它上一行,其實(shí)就是算好哪些內(nèi)存地址上的值,拷貝到哪些內(nèi)存地址,就好了。

這里大家自己看源碼尋找。 所以,有了這個(gè)初始化工作,我們就可以利用這些信息,弄幾個(gè)小算法,實(shí)現(xiàn)各種我們常見控制臺(tái)的操作。

或者換句話說,我們見慣不怪的控制臺(tái),回車、換行、刪除、滾屏、清屏等操作,其實(shí)底層都要實(shí)現(xiàn)相應(yīng)的代碼的。 所以 console.c 中的其他方法就是做這個(gè)事的,我們就不展開每一個(gè)功能的方法體了,簡(jiǎn)單看看有哪些方法。

  1. // 定位光標(biāo)的 
  2. static inline void gotoxy(unsigned int new_x, unsigned int new_y){} 
  3. // 滾屏,即內(nèi)容向上滾動(dòng)一行 
  4. static void scrup(void){} 
  5. // 光標(biāo)同列位置下移一行 
  6. static void lf(int currcons){} 
  7. // 光標(biāo)回到第一列 
  8. static void cr(void){} 
  9. ... 
  10. // 刪除一行 
  11. static void delete_line(void){} 

內(nèi)容繁多,但沒什么難度,只要理解了基本原理即可了。

OK,整個(gè) console.c 就講完了,要知道這個(gè)文件可是整個(gè)內(nèi)核中代碼量最大的文件,可是功能特別單一,也都很簡(jiǎn)單,主要是處理鍵盤各種不同的按鍵,需要寫好多 switch case 等語(yǔ)句,十分麻煩,我們這里就完全沒必要去展開了,就是個(gè)苦力活。 到這里,我們就正式講完了 tty_init 的作用。

在此之后,內(nèi)核代碼就可以用它來方便地在控制臺(tái)輸出字符啦!這在之后內(nèi)核想要在啟動(dòng)過程中告訴用戶一些信息,以及后面內(nèi)核完全建立起來之后,由用戶用 shell 進(jìn)行操作時(shí)手動(dòng)輸入命令,都是可以用到這里的代碼的! 讓我們繼續(xù)向前進(jìn)發(fā),看下一個(gè)被初始化的倒霉鬼是什么東東。 欲知后事如何,且聽下回分解。

本文轉(zhuǎn)載自微信公眾號(hào)「低并發(fā)編程」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系低并發(fā)編程公眾號(hào)。本網(wǎng)站已獲得低并發(fā)編程的授權(quán)。

 

責(zé)任編輯:武曉燕 來源: 低并發(fā)編程
相關(guān)推薦

2021-05-28 08:01:00

JS原型概念

2017-03-09 11:15:18

LinuxRoot賬戶

2020-08-02 22:54:04

Python編程語(yǔ)言開發(fā)

2022-07-26 23:43:29

編程語(yǔ)言開發(fā)Java

2017-12-21 19:38:50

潤(rùn)乾中間表

2021-12-20 14:42:39

程序員職業(yè)技術(shù)

2021-07-29 10:26:34

數(shù)據(jù)分析上云CIO

2022-08-02 18:37:24

BI系統(tǒng)快照表

2016-03-01 15:38:37

微軟鍵盤App

2019-12-02 15:48:13

SSD容量閃存

2019-12-02 14:22:01

浪費(fèi)云計(jì)算支出

2013-01-15 09:41:45

編程語(yǔ)言

2013-01-08 09:40:16

大數(shù)據(jù)軟硬件技術(shù)

2013-01-24 09:44:44

數(shù)據(jù)庫(kù)

2020-10-15 13:19:24

為什么會(huì)存在亂碼

2021-04-15 21:55:38

電腦磁盤微軟

2020-05-28 07:50:18

重排序happens-befCPU

2015-05-18 15:08:08

多種程序設(shè)計(jì)語(yǔ)言程序設(shè)計(jì)語(yǔ)言

2020-06-02 14:17:55

QWER排列鍵盤打印機(jī)

2013-08-01 12:17:21

SAP
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲欧美一区二区三区国产精品 | 国产精品视频免费看 | 一区二区三区国产 | 欧洲精品一区 | 午夜影院黄 | 极情综合网 | 久久精品av麻豆的观看方式 | 91深夜福利视频 | 久久久久久久久91 | 久久免费精品视频 | 欧美日韩三级 | 91久久久久久 | 色婷婷精品国产一区二区三区 | 国产精品视频久久久久久 | 日韩在线免费视频 | 国产综合精品 | 久久久久久久国产精品影院 | 美女视频黄色的 | 免费人成激情视频在线观看冫 | 久久久精品一区二区 | 黄色小视频入口 | 国产成人自拍一区 | 天天操夜夜操免费视频 | 日韩精品在线一区二区 | 亚洲欧美视频一区 | 一级黄色淫片 | 国产成人99久久亚洲综合精品 | 国产一区二区免费电影 | 日本不卡一区二区三区在线观看 | 日韩视频在线免费观看 | 在线观看黄色 | 日韩成人在线视频 | 人人天天操| 亚洲人成在线观看 | 亚洲成人国产综合 | 成人精品鲁一区一区二区 | 精品国产三级 | 北条麻妃一区二区三区在线观看 | 综合色在线 | 欧美精品在线播放 | 亚洲中字在线 |