大家都知道我們可以使用C語言寫一段程序來控制硬件工作,但你知道其工作原理嗎?
1
c語言在實際運行中,都是以匯編指令的方式運行的,由編譯器把C語言編譯成匯編指令,CPU直接執行匯編指令。
所以這個問題就變成,匯編指令是如何操作硬件的?
如果把硬件平臺限制在x86環境下,那么匯編指令操作硬件基本上只有兩種方式:
方式一:
通過向內存空間寫數據。硬件會把硬件上的各種寄存器(外行可以理解為訪問硬件的接口或者操作硬件的工具)映射到某一塊內存地址空間上,之后只要用匯編指令,甚至C語言去讀寫這一段內存地址空間(并非真正操作物理內存),就可以達到操作硬件的目的了。
如果題主還有windowsXP環境(虛擬機也可以),就可以用匯編指令直接操作顯存:
MOV AX,B800
MOV ES,AX
XOR DI,DI
MOV CX,0800
MOV AX,5555
REPZ STOSB
硬件的各種寄存器會被映射到某一塊物理內存中,這種方式稱為MMIO,在Windows的設備管理器里,右鍵點設備,看屬性-》資源里,不少硬件設備都有“內存范圍”的參數,這里的內存范圍就表示這個硬件的資源可以通過訪問這一段內存來控制它。
方式二:
x86匯編中,還有兩個特殊的指令是IN和OUT,這是x86平臺獨有的,上面圖里的I/O范圍,就是用IN/OUT這兩個指令來訪問和控制的。
以上兩種訪問硬件的方式,第一種是可以用C語言實現的,上面一段匯編,本質上類似于C語言代碼:
char ptr = 0xB8000;
int i;
for (i = 0; i 《0x800; i++)
{ptr + i = 0x55;
}
第二種IN/OUT方式沒有直接的C語言語法對應,需要自己封裝匯編。
那么為什么平時很難用C語言操作硬件呢?這是因為平時寫的代碼大多數都在保護模式下,保護模式下,直接訪問物理地址會受到限制,C語言操作的地址都是虛地址。
對于Windows來說,要訪問物理地址,需要工作在內核模式,也就是的寫驅動才行。
而在顯存方面,首先,題主要先明白物理地址和虛擬地址的概念。
原來的8086cpu設計的時候,地址空間有一塊區域(640K-1M)之間,有一塊作為顯存使用
這里你說的預留的地址,是指物理地址,這一段地址的準確范圍是000A0000-000BFFFF,不管是32位還是64位CPU,這一段物理內存地址一直都保留給顯存使用,不區分32位還是64位,也不區分保護模式還是實模式。
可見這一段內存至今仍然是留給顯卡使用的。
那么現在為什么不能直接用這段內存了?
因為現在的軟件都運行在保護模式下,訪問的地址都是虛擬地址,而并非物理地址,包括你使用cmd命令打開的環境,都是虛擬地址,雖然32位XP里能用debug命令向000B8000上寫數據并能顯示在cmd的界面里,但本質上,這都是虛擬出來的。
如果要想用這段顯存怎么辦?
自己寫一個簡易的操作系統,不啟動顯卡的各種圖形加速功能,CPU進入保護模式后在GDT里映射一個4G的數據段,與物理地址一致,那么向000B8000上寫數據,就會像過去DOS一樣顯示在屏幕上,所以保護模式下也可以訪問這一段內存。所以,保護模式下,也可以用它。
顯卡那么多顯存是怎么映射的?
有很多內存地址被映射給顯存了,就是通過這種映射關系,把一些物理地址留給顯存,使得CPU能像訪問內存一樣訪問顯存資源。
當然,實際情況是,2G顯存未必完全映射,而是只映射一部分地址,顯卡有一些開放的寄存器能夠控制哪部分顯存映射過來,這樣就能使得CPU在使用比較少的物理地址范圍的情況下,訪問全部的顯存。
還有一個很有意思的事情:在虛擬機里,找到映射的高地址部分的第一塊內存區域,寫一個能直接訪問物理地址的程序(比如一個驅動),去讀這一塊內存,然后寫到文件里,再用屏幕截圖,也寫到文件里,會發現截圖的內容和顯存里讀出來的內容基本上是一樣的。
2
1 語言層面上,C能直接操作的“硬件”只有內存地址。雖然C支持register關鍵字,但是不能指定某個特定的寄存器,所以只有內存地址。而C中操作內存地址的方式就是指針。例如:
char p = 。..;p = 。..;
2 根據1反推,可以明白如果要開放給C來操作某個硬件,最直接的方案就是設計硬件的時候預先分配好一些固定的地址的用途,然后實際項目中往這些固定地址寫入合法的數據。這樣就可以通過類似
uint32_t p = SCREEN_ADDR;p = RGBA(0xff,0xff,0xff,0xff);
這樣的代碼來實現對硬件的操作了。
3 那這個地址怎么拿到呢?什么樣的數據才是合法的呢?要解答這些問題,就需要查閱具體設備的spec了。例如這個一眼看過去就能的明白的例子(一眼沒看明白請反復閱讀以完全理解上面第二點內容):
我們是用電腦的鍵盤來輸入的指令,每一個指令都對應一個ASCII碼,而這里的ASCII碼就是有序的電壓的高低(或電流的有無,下面只提電壓的高低),即我們輸入的是電壓的高低,你所看到代碼是這些電壓的高低控制顯示器所顯示的圖像,其實電腦也不知道它是什么,只知道這樣顯示。
結論:代碼其實就是存儲在存儲器(內存、硬盤或者閃存等等)中有序的電壓的高低。
再說編譯:
編譯是一個有序的電壓的高低向另一種有序的電壓高低的一種轉換過程,下面以52單片機為例,我們編譯是從表示ASCII碼的那種有序電壓高低轉換為52單片機能夠識別的另一種規定好的有序電壓高低,即表示HEX文件的電壓高低。
結論:編譯出的結果還是電腦中存儲的有序電壓高低。
到單片機燒錄:
接下倆就是燒錄,理解了上面兩點就很容易理解下面的內容,燒錄就是電腦中的有序電壓高低通過數據線傳輸到單片機中的ROM中。
接下來ROM就可以釋放其中的電壓來控制外圍的電路。
總結:從代碼的編輯到最后對電路的控制都是電壓在起作用,只是為了方面我們而給我們展現的形式不一樣而已,而其本質都是電壓,這樣也就不存在轉換。
理解這句話:世界上沒有軟件,軟件只是對硬件的一種反映,就像意識是對世界的一種反映是一樣的!
相信這樣就很容易理解了。
學習從來不是一個人的事情,要有個相互監督的伙伴,工作需要學習C/C++或者為了入行、轉行學習C/C++的伙伴可以私信回復小編“學習”領取全套免費C/C++學習資料、視頻
教程內容包括
1.開發環境搭建
2.C語言教程
3.C++教程
4.數據結構與算法
5..Net全套教程
6.C++Primer教程
7.項目實操