实验1前篇——BIOS编程空间
?????? 實(shí)驗(yàn)1的內(nèi)容是“啟動(dòng)PC系統(tǒng)”——一個(gè)從開機(jī)到運(yùn)行到OS的流程;看似一個(gè)很復(fù)雜的流程,為了很好的解剖這樣的流程,需要充分的知識(shí)準(zhǔn)備,而且更重要的是從代碼的角度去解釋該過程。所以我們將這一篇的內(nèi)容定位與介紹一些概念與模擬代碼,保證理解過程的順利。換句話說,如果在理解實(shí)驗(yàn)1的內(nèi)容有什么問題,可以參考該篇給出的內(nèi)容或者相關(guān)資源。
?????? 本篇主要通過兩個(gè)部分來做出詳細(xì)的介紹:其一為BIOS編程空間;其一為C與匯編的互調(diào)。
一)BIOS編程空間
?????? 這里有一個(gè)很陌生的名詞——編程空間,其實(shí),這是我對(duì)編程環(huán)境的一個(gè)定義,一般用編程環(huán)境來描述,但是它強(qiáng)調(diào)的是編碼的工具與使用的api等概念;但是用編碼空間來代替它,因?yàn)樗粌H僅包含這些內(nèi)容,更多的是強(qiáng)調(diào)編程時(shí)更關(guān)心其代碼運(yùn)行的環(huán)境(內(nèi)存空間,處理器狀態(tài),外設(shè)的資源使用),而且是在一個(gè)固定的環(huán)境中。
??????? 對(duì)于BIOS的編程空間,我們關(guān)注的點(diǎn)主要有如下幾個(gè)方面:
???1.處理器的狀態(tài)
????寄存器的長(zhǎng)度為16位,處于8086的處理器狀態(tài),詳情參考《64-ia-32-architectures-software-developer-manual-325462.pdf》20.1REAL-ADDRESS?MODE
???????2.內(nèi)存的使用
??? 內(nèi)存空間為0-1M的連續(xù)空間,地址訪問方式為(DS<<4|BX);詳情參考《64-ia-32-architectures-software-developer-manual-325462.pdf》20.1REAL-ADDRESS?MODE
??????? 然而不是1M空間都能被任意使用,所以需要理解內(nèi)存映像——內(nèi)存地址區(qū)域的實(shí)際使用情況:
| start? | end? | size? | type? | description? |
| Low?Memory?(the?first?MiB)? | ||||
| 0x00000000? | 0x000003FF? | 1?KiB? | RAM?-?partially?unusable?(see?above)? | Real?Mode?IVT?(Interrupt?Vector?Table)? |
| 0x00000400? | 0x000004FF? | 256?bytes? | RAM?-?partially?unusable?(see?above)? | BDA?(BIOS?data?area)? |
| 0x00000500? | 0x00007BFF? | almost?30?KiB? | RAM?(guaranteed?free?for?use)? | Conventional?memory? |
| 0x00007C00?(typical?location)? | 0x00007DFF? | 512?bytes? | RAM?-?partially?unusable?(see?above)? | Your?OS?BootSector? |
| 0x00007E00? | 0x0007FFFF? | 480.5?KiB? | RAM?(guaranteed?free?for?use)? | Conventional?memory? |
| 0x00080000? | 0x0009FBFF? | approximately?120?KiB,?depending?on?EBDA?size? | RAM?(free?for?use,?if?it?exists)? | Conventional?memory? |
| 0x0009FC00?(typical?location)? | 0x0009FFFF? | 1?KiB? | RAM?(unusable)? | EBDA?(Extended?BIOS?Data?Area)? |
| 0x000A0000? | 0x000FFFFF? | 384?KiB? | various?(unusable)? | Video?memory,?ROM?Area? |
??????? 由上表可以發(fā)現(xiàn),目前0-0x4FF,0x9FC00-0xfffff這兩段內(nèi)存是被系統(tǒng)占用了,不能被我們編程使用。
?????????3.外設(shè)的使用
???? BIOS對(duì)于外設(shè)資源的使用,主要提供了兩種方式:其一為中斷服務(wù),其二為外設(shè)io地址空間。對(duì)于外設(shè)我們主要關(guān)注點(diǎn)為顯示器,鍵盤,串口,硬盤。
??????? 對(duì)于外設(shè)的使用與控制,我覺得一個(gè)很有用的觀點(diǎn)是來之于《PC內(nèi)幕技術(shù)》——資料的來源:
??????? 大多數(shù)情況下,我首先回顧了一下制造商為子系統(tǒng)提供的ic數(shù)據(jù)源清單,然后仔細(xì)察看這些芯片在標(biāo)準(zhǔn)主板上是如何具體連接的,做到這一點(diǎn)需要用到系統(tǒng)的原理圖,在某些情況下,還要查看系統(tǒng)的電路圖。我也仔細(xì)研究了不同制造商提供的反匯編BIOS代碼,以便在這些較低的層次上考察它們與子系統(tǒng)的聯(lián)系。我還生成了一些測(cè)試程序來檢驗(yàn)?zāi)澄髯酉到y(tǒng)的操作。最后,我才去看那些“正式”的文檔,包括IBM的技術(shù)參考資料,當(dāng)然它也是許多其他技術(shù)書籍的資料來源。
??????? 上面的一段話對(duì)如何認(rèn)識(shí)與學(xué)習(xí)ic子系統(tǒng)提供了基本而且嚴(yán)謹(jǐn)?shù)牟襟E與方法,很值得我們每個(gè)人向前輩學(xué)習(xí)。也幸好有前輩們的鋪墊,我們只需要在已經(jīng)有的文檔中與代碼中,理解相關(guān)核心而基本的概念,然后使用總結(jié)好的代碼,保證我們能夠?qū)ο到y(tǒng)有更好的理解與實(shí)現(xiàn)。
???? 如下為詳細(xì)的介紹相關(guān)外設(shè)的知識(shí):
???? A)鍵盤——PS/2?keyboards
作為分析的一個(gè)最簡(jiǎn)單的外設(shè)——主要作為輸入設(shè)備,它的控制芯片為Intel?8042?microcontroller。默認(rèn)了qemu是模擬的IBM?PC/XT?Keyboard?的鍵盤按鍵,其掃描碼詳情見:http://www.computer-engineering.org/ps2keyboard/scancodes1.html
系統(tǒng)組織結(jié)構(gòu)圖如下,詳情可見《PC內(nèi)幕》8.1圖:
鍵盤的基本工作原理:鍵盤檢測(cè)到按鍵按下,然后將按鍵掃描碼通過串口發(fā)送給主板的8042,接著被翻譯為系統(tǒng)掃描碼,放入輸出緩存中,最后通過IRQ9給處理器。
??????? 另外,讀取鍵盤按鍵發(fā)送給主板的時(shí)序如下:
????
如上所述,讀取按鍵的方法有兩種,一種就是通過中斷服務(wù)IRQ?9;另一種為通過io指令讀取8042緩存的按鍵值。
如下提供一種通過io指令讀取按鍵的方法——參考8042提供的io口寄存器配置如下表:
????
| Port | Read?/?? | Function |
| 0x60 | Read | Read?Input?Buffer |
| 0x60 | Write | Write?Output?Buffer |
| 0x64 | Read | Read?Status?Register |
| 0x64 | Write | Send?Command |
如下為讀取按鍵的代碼示例:
kbRead: 1:in $0x64,%al#讀取通訊狀態(tài)andb %al,0x01#檢測(cè)接收按鍵掃描碼是否okjz 1bin $0x60,%al #讀取鍵值
???? 由以上參考讀取按鍵的流程,在讀取鍵值之前需要讀取按鍵是否準(zhǔn)備好,因?yàn)橛梢粋€(gè)通信的過程。
???????? 如果想進(jìn)一步了解,請(qǐng)參考如下的內(nèi)容:
http://www.computer-engineering.org/ps2keyboard/
http://retired.beyondlogic.org/keyboard/keybrd.htm#1
?? 或者參考《IBM.PC.匯編語(yǔ)言程序設(shè)計(jì)(第五版)完整版》第10章,《PC內(nèi)幕技術(shù)》第8章。
??B)串口——UART
?? 對(duì)于串口的理解,我們可以通過兩種方式去理解,其一是屬于數(shù)據(jù)通信的范疇,數(shù)據(jù)通信一般需要考慮的問題:數(shù)據(jù)(幀)格式(數(shù)據(jù)位序LSB),數(shù)據(jù)傳輸速率,出錯(cuò)控制,流量控制;另外,串口又作為系統(tǒng)的外設(shè),又需要中斷控制,一般由如下芯片實(shí)現(xiàn)其功能:
| 芯片編號(hào) | 描述 |
| 8250 | 基本UART功能支持,最早的串口芯片 |
| 8250A/B | 比8250更快 |
| 16450 | 8250的改進(jìn),IBM-AT上使用,支持38.4KBPS |
| 16550 | 在16450基礎(chǔ)上,加入接收與發(fā)送的FIFO |
| 16552 | 支持2個(gè)16550UART |
| 16C454/16C1450/16C1550 | 支持4路16550UART,同時(shí)支持程序可控的掉電與重起 |
| 16650/17650 | 支持更多的FIFO |
?? 所以根據(jù)如上的描述可以得出串口的屬性:
????? (1)波特率——最小波特率,最大波特率
?? 根據(jù)標(biāo)準(zhǔn)的串口的頻率表,可以得到最大的波特率如下:
| 串口頻率 | 1.8432Mhz | 2.4546Mhz |
| 最大波特率 | 115.2?KBPS | 153.6?KBPS |
?????? 如果設(shè)置其他的波特只需要直接分頻即可。
??? 比如:0x03?=??38,400?BPS
?? (2)數(shù)據(jù)格式——數(shù)據(jù)長(zhǎng)度,字節(jié)序(LSB),終止位,奇偶校驗(yàn)
???????
???? (3)流控——FIFO的支持——常見的模式?jīng)]有使用
???? (4)傳輸狀態(tài)——是否傳輸成功與出錯(cuò)
???? (5)中斷設(shè)置——設(shè)置中斷與查詢中斷
????? 根據(jù)如上的屬性,PC系統(tǒng)給出的寄存器表如下,pc系統(tǒng)最多支持4路com
口:
| Name? | Base?Address | IRQ |
| COM?1? | 3F8? | 4 |
| COM?2? | 2F8? | 3 |
| COM?3? | 3E8? | 4 |
| COM?4? | 2E8? | 3 |
??????????????????????????????? 默認(rèn)的com口基地址與IRQ
?
| Base?Address | DLAB | Read/Write | Abr.? | Register?Name? |
| +?0 | =0 | Write | -? | Transmitter?Holding?Buffer? |
| ? | =0 | Read | -? | Receiver?Buffer? |
| ? | =1 | Read/Write | -? | Divisor?Latch?Low?Byte? |
| +?1 | =0 | Read/Write | IER? | Interrupt?Enable?Register? |
| ? | =1 | Read/Write | -? | Divisor?Latch?High?Byte? |
| +?2 | - | Read | IIR? | Interrupt?Identification?Register |
| ? | - | Write | FCR? | FIFO?Control?Register? |
| +?3 | - | Read/Write | LCR? | Line?Control?Register? |
| +?4 | - | Read/Write | MCR? | Modem?Control?Register? |
| +?5 | - | Read | LSR? | Line?Status?Register? |
| +?6 | - | Read | MSR? | Modem?Status?Register? |
| +?7 | - | Read/Write | -? | Scratch?Register? |
?????????????????????????? 支持默認(rèn)com口寄存器
根據(jù)如上表可以看出一個(gè)寄存器由多種功能,其中由一列為DLAB,為一個(gè)開關(guān)設(shè)置波特率的方式;而DLAB的開關(guān)又在LCR的最高位,具體的每個(gè)參數(shù)功能可以參考如下的地址:
http://byterunner.com/16550.html
另外查詢系統(tǒng)支持的com口,可以根據(jù)下表的的內(nèi)存地址讀取相關(guān)的基地址:
| Start?Address | Function |
| 0000:0400 | COM1's?Base?Address |
| 0000:0402 | COM2's?Base?Address |
| 0000:0404 | COM3's?Base?Address |
| 0000:0406 | COM4's?Base?Address |
針對(duì)如上的內(nèi)容解釋可以看出如果要操作串口,有兩種方式,其一為中斷,其二為io指令;一般操作串口的步驟由如下3步:
(1)查詢哪路com能夠被使用,確認(rèn)com的基地址:
movw $0x406,%bx #從com4開始查詢每個(gè)com口是否支持movw $4,%cx check_com:cmpw $0,(%bx)#如果com口的基地址為0,說明該路com口不支持movw (%bx),%ax #讀取com口基地址jne put_com_info next:sub $2,%bxloop check_comjmp 1f
(2)初始化UART的通信狀態(tài):
init_com:OUT_B 0,(PORT1+2) #設(shè)置FCR,FIFO控制為關(guān)OUT_B 0x80,(PORT1 + 3)#開啟DLABOUT_B 0x03,(PORT1 + 0)#設(shè)置最低波特率為38,400 BPSOUT_B 0x00,(PORT1 + 1 ) #設(shè)置最高波特率為115200BPSOUT_B 0x03,(PORT1 + 3) #關(guān)閉DLAB,設(shè)置數(shù)據(jù)位為8位,停止位1位,沒有校驗(yàn)OUT_B 0x01,(PORT1 + 1)#設(shè)置中斷IEROUT_B 0x00,(PORT1 + 4)#關(guān)閉MCRmovw $(PORT1+2),%dx #如下為清楚數(shù)據(jù)緩沖inb %dx,%almovw $(PORT1),%dxinb %dx,%alret
??? (3)發(fā)送串口數(shù)據(jù):
http://retired.beyondlogic.org/serial/serial.htm
????C)硬盤
對(duì)于硬盤的理解,我們需要了解兩方面的知識(shí):1.子系統(tǒng)構(gòu)造,2.磁盤容量。子系統(tǒng)構(gòu)造會(huì)告訴我們控制硬盤的一些基本概念;理解磁盤容量,可以方便我們?nèi)プx取磁盤數(shù)據(jù)。
(1)子系統(tǒng)構(gòu)造框圖——摘錄于《PC內(nèi)幕技術(shù)》第11章簡(jiǎn)介:
????
????
?????上圖我們可以發(fā)現(xiàn)我們讀取磁盤數(shù)據(jù),需要選擇相關(guān)的驅(qū)動(dòng)器。
???? (2)理解磁盤容量,需要知道磁盤的構(gòu)造:
?????
??????? 由上圖可見硬盤主要是由多個(gè)磁盤構(gòu)成,每個(gè)磁盤有2個(gè)磁頭,每個(gè)磁盤可以分為若干個(gè)磁柱,每個(gè)磁柱又由磁道構(gòu)成,磁道由扇區(qū)構(gòu)成。扇區(qū)為512Byte。所以磁盤容量由如下的公式給出:
??? 磁盤容量?=磁頭數(shù)*磁柱數(shù)*磁道扇區(qū)數(shù)*512Byte。
??? 如上的計(jì)算方法為CHS模式。而CHS模式尋址,只有24位,10位C,8位H,6位S。所以支持的最大容量為:1024*255*64*512B=8GByte.
??? 根據(jù)BIOS的容量限制,可以發(fā)現(xiàn)磁盤的最大容量為8GByte,所以這種描述方法不足以描述磁盤容量,所以目前的磁盤支持邏輯塊尋址(LBA),它將整個(gè)磁盤看作連續(xù)的規(guī)定大小的邏輯塊。LBA支持48位,所以它的最大尋址空間為128PB.
??? 更詳細(xì)的了解可以參考如下博客:
http://blog.csdn.net/haiross/article/details/38659825
??????? 當(dāng)我們了解了以上內(nèi)容之后,對(duì)硬盤有如下兩種操作:
?????? (1)讀取硬盤參數(shù):
???????
movb $0x80,%dl #讀取硬盤,0x80表示硬盤,0x00軟盤movb $0x08,%ah #讀取硬盤參數(shù)int $0x13 #13號(hào)磁盤中斷如果讀取成功CF為0具體磁盤參數(shù)如下:
(C,H,S)=(CX[6:15],DH,CL[0-5]).
?DL表示驅(qū)動(dòng)數(shù)
????(2)讀取硬盤數(shù)據(jù):
?? a)通過INT?13?2來進(jìn)行讀取——CHS模式:
disk_2:#讀取mbr到 0:0x800093 movw $0x800,%ax 94 movw %ax,%es95 movw $0x00,%bx96 movb $0x80,%dl97 movb $0x01,%al98 movw $0x01,%cx #讀取第一個(gè)扇區(qū)99 movb $0x00,%dh 100 movb $0x02,%ah #功能2 101 int $0x13 102 mov $0xb4,%ax 103 cmp 0x8000,%ax 104 jnz 1f 105 movb $'y',%al 106 OUT_B0??? b)通過INT?13?0x42來進(jìn)行讀取 —— LBA模式:?
lba_mode:movw $0x80,%dl #讀取第一個(gè)驅(qū)動(dòng)器xorw %ax, %axmovw %ax, 4(%si)#設(shè)置讀取數(shù)據(jù)的內(nèi)存地址incw %ax/* set the mode to non-zero */movb %al, -1(%si)/* the blocks 讀取的大小 */movw %ax, 2(%si)/* the size and the reserved byte */movw $0x0010, (%si)#設(shè)置包的大小與保留字節(jié)/* the absolute address 設(shè)置讀取開始地址*/movl kernel_sector, %ebxmovl %ebx, 8(%si)movl kernel_sector + 4, %ebxmovl %ebx, 12(%si)/* the segment of buffer address 設(shè)置保存數(shù)據(jù)的段地址*/movw $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)movb $0x42, %ahint $0x13???? 詳細(xì)解釋如下:
/*
?*?BIOS?call?"INT?0x13?Function?0x42"?to?read?sectors?from?disk?into?memory
?*Call?with%ah?=?0x42
?*%dl?=?drive?number——驅(qū)動(dòng)號(hào),從(0x80+0開始的驅(qū)動(dòng)號(hào)),一般支持兩個(gè)驅(qū)動(dòng)器0x80|0x81.
?*%ds:%si?=?segment:offset?of?disk?address?packet——發(fā)送數(shù)據(jù)參數(shù)包地址
?*Return:
?*%al?=?0x0?on?success;?err?code?on?failure
?*/
數(shù)據(jù)包格式如下:
struct?Disk_Packet{
unsigned?char?Packat_Size;//1?0x10|0x18
unsigned?char?Reserved0;//1
unsigned?short?Read_Size;//2
unsigned?int*?Buf_Addr;//4?[BX:DS]
unsigned?long?Start_Number;//8
};
??? c)通過io口的方式來讀取磁盤數(shù)據(jù):
??
void waitdisk(void) {// wait for disk reaadywhile ((inb(0x1F7) & 0xC0) != 0x40)/* do nothing */; }void readsect(void *dst, uint32_t offset) {// wait for disk to be readywaitdisk();outb(0x1F2, 1); // count = 1outb(0x1F3, offset);outb(0x1F4, offset >> 8);outb(0x1F5, offset >> 16);outb(0x1F6, (offset >> 24) | 0xE0);outb(0x1F7, 0x20); // cmd 0x20 - read sectors// wait for disk to be readywaitdisk();// read a sectorinsl(0x1F0, dst, SECTSIZE/4); }??? 如果想了解更多,請(qǐng)參考《AT?Attachment?with?Packet?Interface?-?6?(working?draft)》與《PC內(nèi)幕》。
??D)顯示器
??? 對(duì)于顯示器,就更復(fù)雜了。為此我們需要先了解系統(tǒng)的視頻子系統(tǒng)——如下框圖摘錄于《IBM.PC.匯編語(yǔ)言程序設(shè)計(jì)(第五版)完整版》第9章:
???
????? 如上圖可以看出顯示系統(tǒng)的基本原理從視頻顯示區(qū)拿數(shù)據(jù),然后進(jìn)行相關(guān)的處理,最終顯示到顯示器上,所以我們只需要程序去修改視頻顯示區(qū)的數(shù)據(jù)就可以控制顯示器的顯示。而如上顯示原理主要是描述的是“文本模式”的顯示器工作。對(duì)于文本模式的顯示器,描述每個(gè)字符需要兩個(gè)byte來表示:前一個(gè)byte為字符值,后一個(gè)字符為對(duì)應(yīng)的屬性值對(duì)于這種情況可以查看顯示空間的內(nèi)存。屬性值對(duì)應(yīng)如下表:
| 說明 | 背景 | 前景 |
| 屬性 | BL(閃爍)????R??G?B | I(高亮顯示)???????R?????????G???????B |
| 位 | 7??????????????????6???5???4 | 3???????????????????????2????????1??????????0 |
?????? 對(duì)于顯示區(qū)域BIOS給定了固定的地址空間,針對(duì)不同的顯示器有如下3種情況,更詳細(xì)的見表int?13?功能0的表:
??????????A000:[0]?高級(jí)或者復(fù)雜的顯示器
??????????B000:[0]?單色文本
??????????B800:[0]?文本與圖形
?????? 對(duì)于顯示器有兩種操作方式:其一通過中斷顯示數(shù)據(jù),其二,通過將數(shù)據(jù)寫入顯示區(qū)域。對(duì)于“文本模式”的顯示器,還有一個(gè)重要的就是“光標(biāo)”的位置確定。
???? A)查詢系統(tǒng)使用的顯示器?int?13?0f?得到視頻模式
??? 詳細(xì)信息,可以通過bios手冊(cè)查詢得到如下默認(rèn)的CGA(03),如下:
??? 03h?=?T??80x25?8x8???640x200?16??4???B800?CGA,Pcjr,Tandy
??? 如上信息可以知道:
??? 視頻模式為03,且為“文本模式”,支持的字符顯示最大范圍為:80列,25行。每個(gè)字符顯示為8*8的像素,總的屏幕分辨率為640*200.支持16色,最多支持4頁(yè)顯示頁(yè),顯示區(qū)域的開始地址為B8000,支持的顯示器模式為CGA,Pcjr,Tandy。
???B)輸出字符串到屏:
?? (1)通過中斷(int?10h?13h):
?
out_str_to_screen:#輸出字符到屏幕xorw %ax,%ax# movw %cx, %ax#cx->ax,寄存器的需要用% # movw %ax, %ds#初始化數(shù)據(jù)段movw %ax, %es#初始化擴(kuò)展段 #如下位利用BIOS的中斷0x10來實(shí)現(xiàn)輸出字符串到屏幕上 #詳情功能定義可以參考bios手冊(cè) #中斷:int 0x10 #功能號(hào):ah = 0x13,輸出字符串到屏幕 #屬性:es:bp表示輸出的字符串 # cx表示輸出字符串長(zhǎng)度 # dh,dl表示顯示的行與列 #movw $msgstr,%bp#將msgstr的地址放入bp,地址的表示為$movw len, %cx #將字符串的長(zhǎng)度放入cx,標(biāo)志的值直接用標(biāo)識(shí)符movb $0x01, %al #movb $0x01, %bl #字符屬性movb $0x00, %bh #第0頁(yè)movb $0x05, %dh #第5行 movb $0x08, %dl #第8列movb $0x13, %ah # 功能13,輸出字符串int $0x10 #調(diào)用中斷10h?? (2)直接寫顯示區(qū)域
????
print_str:#通過直接寫顯示區(qū)域的方式,輸出字符xorw %bx,%bxmovb $'T',%almovb $0xCE,%ahmovw $0xB800,%cxmovw %cx,%dsmovw %ax,(5*80*2+19*2)(%bx)#第5行第24個(gè)字符??? C)獲取與修改光標(biāo)位置:
? (1)通過中斷(int?10h?03h):
read_cursor: #讀取光標(biāo)位置movb $0x0,%bhmovb $0x03,%ahint $0x10
? (2)通過io指令讀取:
?????http://www.osdever.net/FreeVGA/home.htm
??
????????對(duì)于第1,2點(diǎn)的詳細(xì)了解,我們才能知道如何編寫代碼,然后執(zhí)行之。而BIOS的編程之后的代碼執(zhí)行是在mbr中,所以我們需要搭建bios實(shí)驗(yàn)環(huán)境。然后調(diào)試我們的外設(shè)的相關(guān)代碼。所以我們的最初代碼執(zhí)行空間也只有512Btye(0x7C00-0x7DFF),如果要使用更多的代碼,需要將它們放到磁盤的后續(xù)分區(qū),然后被加載到內(nèi)存中,才能使用。對(duì)于如上描述可以參考我博客的附件資源,其中包含了所有的實(shí)現(xiàn),方便理解。
????另外推薦一份bios的參考手冊(cè),絕對(duì)詳細(xì):
http://www.cs.cmu.edu/~ralf/files.html
??? 關(guān)于中斷的參考手冊(cè):
http://www.htl-steyr.ac.at/~morg/pcinfo/hardware/interrupts/inte1at0.htm
??二)C與匯編的互調(diào)
? 在系統(tǒng)一上電運(yùn)行的代碼肯定是匯編語(yǔ)言,等到將c語(yǔ)言運(yùn)行環(huán)境創(chuàng)建好了之后才能運(yùn)行之。所以需要了解如下幾個(gè)方面的知識(shí):
?? 1.首先匯編代碼如何被執(zhí)行
?? 根據(jù)上一節(jié)描述,我們寫的代碼會(huì)放在mbr里,當(dāng)然如果突破了mbr的限制,其他代碼可以放到硬盤的其他扇區(qū)。在mbr的代碼會(huì)被自動(dòng)加載的0x7C00中執(zhí)行,所以我們需要將我們的代碼鏈接到0x7C00,查看makefile會(huì)發(fā)現(xiàn)可以這樣實(shí)現(xiàn):
?? A.直接鏈接生成mbr
? B.鏈接成elf文件后,再轉(zhuǎn)換:
?
read_sector.out:read_sector.old -N -Ttext 0x8400 -o $@.bak $< -e readsect -m elf_i386 #這里是鏈接到地址0x8400,進(jìn)入點(diǎn)為readsectobjcopy -S -O binary -j .text $@.bak $@#將生成read_sector.out.bak 轉(zhuǎn)換成 read_sector.out。??? 方法2比方法1的優(yōu)勢(shì)是:a.文件更小,b.生成的elf文件可以被反匯編分析。
? 方法1的優(yōu)勢(shì):簡(jiǎn)單。
?? 關(guān)于mbr的詳細(xì)介紹可以參考百度百科:
http://baike.baidu.com/link?url=sQhyg1wSqI1n3JgBuueAkh9NUlQ6iCG5a0HsfVexr4Ky3gwDfgxYBzsMwGZTcCzOqom8wfuxG62WxNYsP6WB6zOcdvocdfRtSUnKEkVC0Ji
? 它的解析,有附件的一個(gè)c語(yǔ)言文件。獲取本機(jī)系統(tǒng)的mbr可以用如下命令:
sudo dd if=/dev/sda of=mbr bs=512 count=1? 2.匯編如何調(diào)用c語(yǔ)言:
? 匯編為了調(diào)用c語(yǔ)言,需要做兩步工作,第一創(chuàng)建c語(yǔ)言運(yùn)行環(huán)境,第二調(diào)用c語(yǔ)言入口代碼。
?? a)創(chuàng)建c運(yùn)行環(huán)境:
???因?yàn)閏語(yǔ)言的基本結(jié)構(gòu)是基于堆棧的,所以首先要?jiǎng)?chuàng)建堆棧:
init_stack:xorw %ax,%axmovw %ax,%es #設(shè)置堆棧段movw $0x7c00,%sp #設(shè)置棧頂?? 再c語(yǔ)言調(diào)用過程中肯定會(huì)讀寫全局?jǐn)?shù)據(jù),所以需要設(shè)置對(duì)應(yīng)的數(shù)據(jù)段: xorw %bp,%bp #清除bp,因?yàn)樗鼤?huì)被c語(yǔ)言用于棧幀的開始指向。movw %bp,%ds #設(shè)置數(shù)據(jù)段movw %bp,%es #設(shè)置擴(kuò)展段?? b)調(diào)用c語(yǔ)言,傳入?yún)?shù),然后調(diào)用對(duì)應(yīng)的地址就可以了: pushl $0 #設(shè)置第2個(gè)參數(shù)pushl $0x8000 #設(shè)置第一個(gè)參數(shù)calll 0x843b #調(diào)用c語(yǔ)言入口的函數(shù)地址?? 注意傳遞參數(shù)的順序是,從右到左。?? 3.c語(yǔ)言如何調(diào)用匯編語(yǔ)言:
?? 通過AT&T內(nèi)嵌匯編語(yǔ)法調(diào)用:
static __inline void outb(int port, uint8_t data) {__asm __volatile("outb %0,%w1" : : "a" (data), "d" (port)); } static __inline uint8_t inb(int port) {uint8_t data;__asm __volatile("inb %w1,%0" : "=a" (data) : "d" (port));return data; }??? C語(yǔ)言使用inline的方法,聲明函數(shù)為內(nèi)斂: static __inline uint8_t inb(int port) <strong>__attribute__((always_inline))</strong>; static __inline void insl(int port, void *addr, int cnt) __attribute__((always_inline)); static __inline void outb(int port, uint8_t data) __attribute__((always_inline));?? 4.匯編語(yǔ)言編程的技術(shù)總結(jié)
????? 這里講的匯編語(yǔ)言主要是unix標(biāo)準(zhǔn)下的AT&T語(yǔ)法,簡(jiǎn)單來說與Intel的語(yǔ)法有差別的地方與特定的偽指令支持,為什么需要了解AT&T匯編呢?因?yàn)樵趌inux平臺(tái)上,甚至Unix下,都適用,它與硬件平臺(tái)無關(guān),所以適用性很強(qiáng)。
? a.支持的指令格式,操作數(shù)與Intel匯編相反,比如:
? Intel?:mov?eax,?ebx?#ebx->(賦值給)eax
? AT&T??:mov?%ebx,%eax
? 編譯器會(huì)檢測(cè)操作順序,如果不能匹配,則出錯(cuò)。
? b.Intel的指令,AT&T都支持,所以對(duì)于一些不了解的指令,可以參考Intel的相關(guān)技術(shù)手冊(cè)。
? c.操作數(shù)的表示格式
? 寄存器前需要添加%
? 立即數(shù)需要添加$
? 默認(rèn)常數(shù)為地址
??? d.對(duì)宏的支持:
.macro OUT_B v pa #定義宏——將v輸出到端口pa,參數(shù)為v,pa#通過宏定義,將參數(shù)v轉(zhuǎn)化為簡(jiǎn)單的表達(dá),換句話說,在宏定義中,參數(shù)的引用為\v。 #define V \v #define PA \pamovb $(V),%al #將V當(dāng)作立即數(shù)使用movw $(PA),%dxoutb %al,%dx .endm????? f.運(yùn)行數(shù)據(jù)的保存
???? 因?yàn)?span style="font-family:Times New Roman">intel只有8個(gè)寄存器EAX,EBX,?ECX,?EDX,?ESI,?EDI,ESP,EBP可用,而且很多時(shí)候都是受限的——即特定的寄存器有特定的用途,比如:相對(duì)尋址只有用EBX。所以,寄存器的資源是不夠的,需要用堆棧來保存:
??? g.對(duì)地址空間的使用:
???? 如上的內(nèi)容可以參考《gas_manual-unix匯編》與我附件的代碼。
??? 一葉說:終于寫完了,內(nèi)容太多了,而且需要一步步謹(jǐn)慎的驗(yàn)證;如上內(nèi)容只是截取了主要的知識(shí)介紹,對(duì)我們后期理解系統(tǒng)的運(yùn)行流程與實(shí)現(xiàn)相關(guān)代碼有幫助;請(qǐng)理解它們,請(qǐng)調(diào)試它們。如果需要更進(jìn)一步了解相關(guān)內(nèi)容,請(qǐng)參考詳實(shí)附件,這樣對(duì)理解整個(gè)系統(tǒng)與相關(guān)軟件的工作原理更有意義。最后還要感謝前輩們的知識(shí)總結(jié),也希望后來的我們能夠站在它們的肩膀上,爬得更高。
總結(jié)
以上是生活随笔為你收集整理的实验1前篇——BIOS编程空间的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 尾气冒黑烟是什么问题_汽车排气管冒黑烟,
- 下一篇: 观天利器-stellarium