鍵盤--掃描碼--ASCII碼--顯示器上的字符

在上一篇,我講了鍵盤操做會產生掃描碼以及如何解析Pause鍵和Print Screen鍵的掃描碼。linux

在這一篇,我會說清楚」鍵盤上的輸入爲何會出如今顯示器上「。數組

極簡版

  1. 咱們敲擊鍵盤,產生掃描碼。
  2. 操做系統獲取掃描碼,把掃描碼解析成ASCII碼。
  3. 操做系統把ASCII碼寫入顯存,顯示器上就會打印出顯存中的可打印字符。

解析掃描碼

在上一篇,我創建了一個Make Code和按鍵ASCII碼(部分按鍵如Enter是我本身設置的值)的映射數組keyboardMapspa

按鍵不是Pause,也不是Print Screen,就進入下面的解析流程。操作系統

默認鍵值

  1. 當接收到的掃描碼只有一個,
  2. 掃描碼的類型是Make Code,
  3. 這個Make Code的值是V
  4. 那麼鍵值是keyboardMap[V *3]

另外一個鍵值

  1. 接收到的第一個掃描碼是Make Code,值是V
  2. keyboardMap中查詢出被按下的鍵是left_shiftright_shift
  3. 設置column的值是1。
  4. 接收到第二個掃描碼是Make Code,值是N
  5. 從映射數組中查詢這個掃描碼對應的鍵值是:keyboardMap[N * 3 + column]

注意,查詢到的鍵值是keyboardMap[N * 3 + column]。這就是鍵盤上的1A這類按鍵與shift鍵組合時的值,即默認值外的另外一個值。命令行

提問

請想想,點擊shift + A鍵,讀取掃描碼的過程是什麼樣的?設計

Enter

Enter鍵和Backspace鍵很容易解析。獲取掃描碼(Make Code)S,獲取鍵值keyboardMap[N * 3]rest

根據鍵值識別出是Enter鍵後就把光標設置到下一行。code

識別出是Backspace就這樣處理最新的兩個字節:將高字節設置成0Fh,將低字節設置成空字符的ASCII碼;另外,將光標的位置後退兩個字符。blog

Caps Lock等鍵

沒有弄明白。索引

清屏

使用linux的命令行、顯示器上打印滿了字符,咱們能夠使用clear讓命令行終端的字符所有消失。讓屏幕上的字符所有消失,這就是清屏。

clear命令只是移動了光標的位置,並無清除屏幕上的字符。按下方向鍵中的Up能定位到已經消失的字符。

咱們的清屏,是讓字符完全從屏幕上消失,不管怎麼按Up鍵都不會再看到字符。

清屏其實很簡單,就是往寫滿字符的顯存區域寫入空字符。

tty

什麼是tty

我以爲tty是一個比較過期的功能,我幾乎沒用過。

想體驗一番tty是什麼的同窗,能夠在安裝了linux系統的電腦或虛擬機上按下shift + alt + f1shift + alt + f2鍵在不一樣的tty之間切換。下面是我在虛擬機上切換tty的效果,一個是圖形界面,一個是純命令行界面。

image-20210312211841834.png

image-20210312212013507.png

VGA

我只講述VGA模式下的tty。

如上圖所示,不一樣的tty窗口展現的內容徹底不相同,在tty0窗口敲擊鍵盤,數據只會出如今tty0的顯示器;切換到tty1後,顯示器上不會出現剛剛敲擊鍵盤的數據;反過來也是同樣。

不一樣tty之間,是徹底隔離的,只是公用一個鍵盤。

VGA是什麼呢?我以爲弄清楚這個概念的方方面面沒有更多的做用。對VGA,瞭解有限的下面這些有限的知識就夠了。

字符顯示

在這種模式下,顯示器一共能顯示25行字符,每行80個字符。

每一個字符佔用2個字節,高字節是字符的顏色,低字節是字符的ASCII碼。例如

mov		ah,		0Ah
mov		al,		'A'

mov ah, 0Ah 中的 0Ah 的高4位和低4位分別是: 0000b1010b。它們分別設置字符的背景色和前景色。

mov al, 'A' 中的 A 是要打印的字符。

寄存器

VGA模式下的顯示器是一個硬件,提供了多組寄存器。

讀寫這些寄存器,能設置光標的位置、點擊Up等方向鍵能滾屏。

讀寫這類寄存器,需先往一類寄存器中寫入另外一類寄存器的索引,即往另外一類寄存器的第N個寄存器中寫入數據;而後往第二類寄存器寫入數據,不須要再指定具體是哪一個寄存器。

tty的實現

顯示器上的內容是顯存中的數據的映射。要讓不一樣的tty窗口打印不一樣的內容,只需把顯存分割成若干塊,每一個tty分配一塊。

顯示器一滿屏所需空間是80 * 25 * 2 = 4000個字節。顯存的內存地址是0xB8000~~~0xBFFFF,總計0xBFFFF - 0xB8000 + 1 = 0x8000 = 32768字節。若是實現3個tty,每一個tty對應的顯存大小大概是32768 / 3 = 10922個字節,能存儲兩」滿屏「數據。

僞代碼

每一個tty設計一個緩衝區C1,數據從鍵盤緩衝區C2到這個緩衝區,而後再從C1讀取數據寫入對應tty的顯存區域。

tty對應的顯存區域,也設計一個結構來存儲,叫Console

直接看僞代碼吧。

typedef		struct{
  	// tty使用的顯存的開始位置
  	int		original_address;
  	// tty使用的顯存的大小
  	int		limit;
  	// tty使用顯存的當前位置
  	int		current_address;
  	// tty的光標位置
  	int		cursor_address;
}Console;

typedef		struct{
  		// 緩衝區下一個要處理的字符的
  		int		tail;
  		// 緩衝區下一個空閒位置
  		int		head;
  		// 緩衝區存儲的數據的數量
  		int		count;
  		// 存儲數據的數組
  		int		buff[256];
  
  		Console * console;
}TTY;

全局視角下的流程

  1. tty任務和其餘用戶進程(TestA等)一塊兒初始化,並使用restart運行tty任務。
  2. tty任務的流程以下:
    1. tty任務遍歷全部的tty窗口。
    2. 若是被遍歷到的tty窗口是當前tty,從C1緩衝區讀取數據D。
    3. 把D轉換成ASCII碼,而後交給寫顯存模塊打印到顯示器。
  3. 發生時鐘中斷,給tty任務創建快照,調度模塊讓用戶進程上CPU運行。
  4. 用戶敲擊鍵盤,8048監測到鍵盤操做,讀取掃描碼(第2套),傳輸給8042。
  5. 8042把掃描碼(第2套)轉換成第1套,放入鍵盤緩衝區C2,通知8259A發生鍵盤中斷。
    1. 在C2中的數據被取走前,8042再也不接收新數據。
  6. 鍵盤中斷例程save當前進程,從C2讀取數據,而後放入C1。
  7. tty任務在某個時刻得到CPU控制權,執行第2步的流程。
相關文章
相關標籤/搜索