實驗1前篇——BIOS編程空間

      從此篇開始更多的注重技術細節了。細節決定成敗的故事太多了,而且對於編程者肯定都有這樣的經歷——自信滿滿的認爲自己寫得程序天衣無縫,但是當運行調試之後,才發現bug無處不在,更可氣的是很多的bug卻是因爲語法細節或者指尖上的失誤導致。而且編碼效率的提升,更多的是建立在已經存在的可信代碼的基礎上,而可信代碼卻是經歷過無數次的折騰的結晶。

       實驗1的內容是啓動PC系統”——一個從開機到運行到OS的流程;看似一個很複雜的流程,爲了很好的解剖這樣的流程,需要充分的知識準備,而且更重要的是從代碼的角度去解釋該過程。所以我們將這一篇的內容定位與介紹一些概念與模擬代碼,保證理解過程的順利。換句話說,如果在理解實驗1的內容有什麼問題,可以參考該篇給出的內容或者相關資源。

       本篇主要通過兩個部分來做出詳細的介紹:其一爲BIOS編程空間;其一爲C與彙編的互調。

一)BIOS編程空間

       這裏有一個很陌生的名詞——編程空間,其實,這是我對編程環境的一個定義,一般用編程環境來描述,但是它強調的是編碼的工具與使用的api等概念;但是用編碼空間來代替它,因爲它不僅僅包含這些內容,更多的是強調編程時更關心其代碼運行的環境(內存空間,處理器狀態,外設的資源使用),而且是在一個固定的環境中。

        對於BIOS的編程空間,我們關注的點主要有如下幾個方面:

   1.處理器的狀態

    寄存器的長度爲16位,處於8086的處理器狀態,詳情參考《64-ia-32-architectures-software-developer-manual-325462.pdf》20.1REAL-ADDRESS MODE

       2.內存的使用

    內存空間爲0-1M的連續空間,地址訪問方式爲(DS<<4|BX);詳情參考《64-ia-32-architectures-software-developer-manual-325462.pdf》20.1REAL-ADDRESS MODE

        然而不是1M空間都能被任意使用,所以需要理解內存映像——內存地址區域的實際使用情況:


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 


        由上表可以發現,目前0-0x4FF,0x9FC00-0xfffff這兩段內存是被系統佔用了,不能被我們編程使用。

         3.外設的使用

     BIOS對於外設資源的使用,主要提供了兩種方式:其一爲中斷服務,其二爲外設io地址空間。對於外設我們主要關注點爲顯示器,鍵盤,串口,硬盤。

        對於外設的使用與控制,我覺得一個很有用的觀點是來之於《PC內幕技術》——資料的來源:

        大多數情況下,我首先回顧了一下製造商爲子系統提供的ic數據源清單,然後仔細察看這些芯片在標準主板上是如何具體連接的,做到這一點需要用到系統的原理圖,在某些情況下,還要查看系統的電路圖。我也仔細研究了不同製造商提供的反彙編BIOS代碼,以便在這些較低的層次上考察它們與子系統的聯繫。我還生成了一些測試程序來檢驗某西子系統的操作。最後,我纔去看那些正式的文檔,包括IBM的技術參考資料,當然它也是許多其他技術書籍的資料來源。

        上面的一段話對如何認識與學習ic子系統提供了基本而且嚴謹的步驟與方法,很值得我們每個人向前輩學習。也幸好有前輩們的鋪墊,我們只需要在已經有的文檔中與代碼中,理解相關核心而基本的概念,然後使用總結好的代碼,保證我們能夠對系統有更好的理解與實現。

     如下爲詳細的介紹相關外設的知識:

     A)鍵盤——PS/2 keyboards

作爲分析的一個最簡單的外設——主要作爲輸入設備,它的控制芯片爲Intel 8042 microcontroller。默認了qemu是模擬的IBM PC/XT Keyboard 的鍵盤按鍵,其掃描碼詳情見:http://www.computer-engineering.org/ps2keyboard/scancodes1.html

系統組織結構圖如下,詳情可見《PC內幕》8.1圖:

鍵盤的基本工作原理:鍵盤檢測到按鍵按下,然後將按鍵掃描碼通過串口發送給主板的8042,接着被翻譯爲系統掃描碼,放入輸出緩存中,最後通過IRQ9給處理器。

        另外,讀取鍵盤按鍵發送給主板的時序如下:

    

如上所述,讀取按鍵的方法有兩種,一種就是通過中斷服務IRQ 9;另一種爲通過io指令讀取8042緩存的按鍵值。

如下提供一種通過io指令讀取按鍵的方法——參考8042提供的io口寄存器配置如下表:

    

Port

Read /  
Write

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#讀取通訊狀態
        andb %al,0x01#檢測接收按鍵掃描碼是否ok
        jz 1b
        in $0x60,%al #讀取鍵值

     由以上參考讀取按鍵的流程,在讀取鍵值之前需要讀取按鍵是否準備好,因爲由一個通信的過程。

         如果想進一步瞭解,請參考如下的內容:

http://www.computer-engineering.org/ps2keyboard/

http://retired.beyondlogic.org/keyboard/keybrd.htm#1

   或者參考《IBM.PC.彙編語言程序設計(第五版)完整版》第10章,《PC內幕技術》第8章。

  B)串口——UART

   對於串口的理解,我們可以通過兩種方式去理解,其一是屬於數據通信的範疇,數據通信一般需要考慮的問題:數據(幀)格式(數據位序LSB),數據傳輸速率,出錯控制,流量控制;另外,串口又作爲系統的外設,又需要中斷控制,一般由如下芯片實現其功能:

芯片編號

描述

8250

基本UART功能支持,最早的串口芯片

8250A/B

比8250更快

16450

8250的改進,IBM-AT上使用,支持38.4KBPS

16550

在16450基礎上,加入接收與發送的FIFO

16552

支持2個16550UART

16C454/16C1450/16C1550

支持4路16550UART,同時支持程序可控的掉電與重起

16650/17650

支持更多的FIFO

   所以根據如上的描述可以得出串口的屬性:

      (1)波特率——最小波特率,最大波特率

   根據標準的串口的頻率表,可以得到最大的波特率如下:

串口頻率

1.8432Mhz

2.4546Mhz

最大波特率

115.2 KBPS

153.6 KBPS

       如果設置其他的波特只需要直接分頻即可。

    比如:0x03 =  38,400 BPS

   (2)數據格式——數據長度,字節序(LSB),終止位,奇偶校驗

       

     (3)流控——FIFO的支持——常見的模式沒有使用

     (4)傳輸狀態——是否傳輸成功與出錯

     (5)中斷設置——設置中斷與查詢中斷

      根據如上的屬性,PC系統給出的寄存器表如下,pc系統最多支持4com

口:

Name 

Base Address

IRQ

COM 1 

3F8 

4

COM 2 

2F8 

3

COM 3 

3E8 

4

COM 4 

2E8 

3

                                默認的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 

                           支持默認com口寄存器

根據如上表可以看出一個寄存器由多種功能,其中由一列爲DLAB,爲一個開關設置波特率的方式;而DLAB的開關又在LCR的最高位,具體的每個參數功能可以參考如下的地址:

http://byterunner.com/16550.html

另外查詢系統支持的com,可以根據下表的的內存地址讀取相關的基地址:

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

針對如上的內容解釋可以看出如果要操作串口,有兩種方式,其一爲中斷,其二爲io指令;一般操作串口的步驟由如下3步:

(1)查詢哪路com能夠被使用,確認com的基地址:

 movw $0x406,%bx #從com4開始查詢每個com口是否支持
        movw $4,%cx
check_com:
        cmpw $0,(%bx)#如果com口的基地址爲0,說明該路com口不支持
        movw (%bx),%ax #讀取com口基地址
        jne put_com_info
next:
        sub $2,%bx
        loop check_com
        jmp 1f

(2)初始化UART的通信狀態:


init_com:
        OUT_B 0,(PORT1+2) #設置FCR,FIFO控制爲關
        OUT_B 0x80,(PORT1 + 3)#開啓DLAB
        OUT_B 0x03,(PORT1 + 0)#設置最低波特率爲38,400 BPS
        OUT_B 0x00,(PORT1 + 1 ) #設置最高波特率爲115200BPS
        OUT_B 0x03,(PORT1 + 3) #關閉DLAB,設置數據位爲8位,停止位1位,沒有校驗
        OUT_B 0x01,(PORT1 + 1)#設置中斷IER
        OUT_B 0x00,(PORT1 + 4)#關閉MCR
        movw $(PORT1+2),%dx #如下爲清楚數據緩衝
        inb %dx,%al
        movw $(PORT1),%dx
        inb %dx,%al
        ret

    (3)發送串口數據:


out_char_to_console:
	1:
        movw $(PORT1+5),%dx #讀取LSR狀態,是否發送完畢
        inb %dx,%al
        andb %al,0x20 #發送緩衝爲空
        jz 1b
	   movb $'a',%al
	   movw $PORT1,%dx 
        outb %al,%dx #發送數據
        ret
    如果需要詳細瞭解,請參考如下地址:

http://retired.beyondlogic.org/serial/serial.htm

    C)硬盤

對於硬盤的理解,我們需要了解兩方面的知識:1.子系統構造,2.磁盤容量。子系統構造會告訴我們控制硬盤的一些基本概念;理解磁盤容量,可以方便我們去讀取磁盤數據。

(1)子系統構造框圖——摘錄於《PC內幕技術》第11章簡介:

    

    

     上圖我們可以發現我們讀取磁盤數據,需要選擇相關的驅動器。

     (2)理解磁盤容量,需要知道磁盤的構造:

     

        由上圖可見硬盤主要是由多個磁盤構成,每個磁盤有2個磁頭,每個磁盤可以分爲若干個磁柱,每個磁柱又由磁道構成,磁道由扇區構成。扇區爲512Byte。所以磁盤容量由如下的公式給出:

    磁盤容量 =磁頭數*磁柱數*磁道扇區數*512Byte。

    如上的計算方法爲CHS模式。而CHS模式尋址,只有24位,10位C,8位H,6位S。所以支持的最大容量爲:1024*255*64*512B=8GByte.

    根據BIOS的容量限制,可以發現磁盤的最大容量爲8GByte,所以這種描述方法不足以描述磁盤容量,所以目前的磁盤支持邏輯塊尋址(LBA),它將整個磁盤看作連續的規定大小的邏輯塊。LBA支持48位,所以它的最大尋址空間爲128PB.

    更詳細的瞭解可以參考如下博客:

http://blog.csdn.net/haiross/article/details/38659825

        當我們瞭解了以上內容之後,對硬盤有如下兩種操作:

       (1)讀取硬盤參數:

       

movb $0x80,%dl #讀取硬盤,0x80表示硬盤,0x00軟盤
     movb $0x08,%ah #讀取硬盤參數
     int $0x13 #13號磁盤中斷

如果讀取成功CF0具體磁盤參數如下:

CHS=(CX[6:15],DH,CL[0-5]).

 DL表示驅動數

    (2)讀取硬盤數據:

   a)通過INT 13 2來進行讀取——CHS模式:

disk_2:#讀取mbr到 0:0x8000
 93         movw $0x800,%ax 
 94         movw %ax,%es
 95         movw $0x00,%bx
 96         movb $0x80,%dl
 97         movb $0x01,%al
 98         movw $0x01,%cx #讀取第一個扇區
 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來進行讀取——LBA模式:

 

lba_mode:
	movw	$0x80,%dl #讀取第一個驅動器
	xorw	%ax, %ax
	movw	%ax, 4(%si)#設置讀取數據的內存地址

	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)#設置包的大小與保留字節

	/* the absolute address 設置讀取開始地址*/
	movl	kernel_sector, %ebx
	movl	%ebx, 8(%si)
	movl	kernel_sector + 4, %ebx
	movl	%ebx, 12(%si)

	/* the segment of buffer address 設置保存數據的段地址*/
	movw	$GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)

	movb	$0x42, %ah
	int	$0x13

     詳細解釋如下:

/*

 * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory

 *Call with%ah = 0x42

 *%dl = drive number——驅動號,從(0x80+0開始的驅動號),一般支持兩個驅動器0x80|0x81.

 *%ds:%si = segment:offset of disk address packet——發送數據參數包地址

 *Return:

 *%al = 0x0 on success; err code on failure

 */

數據包格式如下:

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口的方式來讀取磁盤數據:

  

void
waitdisk(void)
{
	// wait for disk reaady
	while ((inb(0x1F7) & 0xC0) != 0x40)
		/* do nothing */;
}

void
readsect(void *dst, uint32_t offset)
{
	// wait for disk to be ready
	waitdisk();

	outb(0x1F2, 1);		// count = 1
	outb(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 ready
	waitdisk();

	// read a sector
	insl(0x1F0, dst, SECTSIZE/4);
}

    如果想了解更多,請參考《AT Attachment with Packet Interface - 6 (working draft)》與《PC內幕》。

  D)顯示器

    對於顯示器,就更復雜了。爲此我們需要先了解系統的視頻子系統——如下框圖摘錄於《IBM.PC.彙編語言程序設計(第五版)完整版》第9章:

   

      如上圖可以看出顯示系統的基本原理從視頻顯示區拿數據,然後進行相關的處理,最終顯示到顯示器上,所以我們只需要程序去修改視頻顯示區的數據就可以控制顯示器的顯示。而如上顯示原理主要是描述的是文本模式的顯示器工作。對於文本模式的顯示器,描述每個字符需要兩個byte來表示:前一個byte爲字符值,後一個字符爲對應的屬性值對於這種情況可以查看顯示空間的內存。屬性值對應如下表:

說明

背景

前景

屬性

BL(閃爍)    R  G B

I(高亮顯示)       R         G       B

7                  6   5   4

3                       2        1          0

       對於顯示區域BIOS給定了固定的地址空間,針對不同的顯示器有如下3種情況,更詳細的見表int 13 功能0的表:

          A000:[0] 高級或者複雜的顯示器

          B000:[0] 單色文本

          B800:[0] 文本與圖形

       對於顯示器有兩種操作方式:其一通過中斷顯示數據,其二,通過將數據寫入顯示區域。對於“文本模式”的顯示器,還有一個重要的就是“光標”的位置確定。

     A)查詢系統使用的顯示器 int 13 0f 得到視頻模式

    詳細信息,可以通過bios手冊查詢得到如下默認的CGA(03),如下:

    03h = T  80x25 8x8   640x200 16  4   B800 CGA,Pcjr,Tandy

    如上信息可以知道:

    視頻模式爲03,且爲“文本模式”,支持的字符顯示最大範圍爲:80列,25行。每個字符顯示爲8*8的像素,總的屏幕分辨率爲640*200.支持16色,最多支持4頁顯示頁,顯示區域的開始地址爲B8000,支持的顯示器模式爲CGA,Pcjr,Tandy。

   B)輸出字符串到屏:

   (1)通過中斷(int 10h 13h)

 

out_str_to_screen:#輸出字符到屏幕
    xorw   %ax,%ax
   # movw    %cx,    %ax#cx->ax,寄存器的需要用%  
   # movw    %ax,    %ds#初始化數據段
    movw    %ax,    %es#初始化擴展段
#如下位利用BIOS的中斷0x10來實現輸出字符串到屏幕上
#詳情功能定義可以參考bios手冊
#中斷:int 0x10
#功能號:ah = 0x13,輸出字符串到屏幕
#屬性:es:bp表示輸出的字符串
#	cx表示輸出字符串長度
#	dh,dl表示顯示的行與列
#
    movw    $msgstr,%bp#將msgstr的地址放入bp,地址的表示爲$
    movw    len,    %cx  #將字符串的長度放入cx,標誌的值直接用標識符
    movb    $0x01,  %al  #
    movb    $0x01,  %bl  #字符屬性
    movb    $0x00,  %bh  #第0頁
    movb    $0x05,  %dh #第5行 
    movb    $0x08,  %dl  #第8列
    movb    $0x13,  %ah # 功能13,輸出字符串
  
    int     $0x10  #調用中斷10h

   (2)直接寫顯示區域

    

print_str:#通過直接寫顯示區域的方式,輸出字符
    xorw    %bx,%bx
    movb    $'T',%al
    movb    $0xCE,%ah
    movw    $0xB800,%cx
    movw    %cx,%ds
    movw    %ax,(5*80*2+19*2)(%bx)#第5行第24個字符

    C)獲取與修改光標位置:

  (1)通過中斷(int 10h 03h):

read_cursor: #讀取光標位置
	movb $0x0,%bh
	movb $0x03,%ah
	int $0x10

  (2)通過io指令讀取:


set_cursor: #設置光標
    OUT_B 14,0x3D4 #切換到第5行第24個字符的位置
    OUT_B (5*80+19)/256,0x3D5
   OUT_B 15,0x3D4
   OUT_B (5*80+19)%256,0x3D5
  更詳細的介紹可以參考如下網址,或者參考《PC內幕》或者《IBM.PC.彙編語言程序設計(第五版)完整版》:

     http://www.osdever.net/FreeVGA/home.htm


  

        對於第1,2點的詳細瞭解,我們才能知道如何編寫代碼,然後執行之。而BIOS的編程之後的代碼執行是在mbr中,所以我們需要搭建bios實驗環境。然後調試我們的外設的相關代碼。所以我們的最初代碼執行空間也只有512Btye(0x7C00-0x7DFF),如果要使用更多的代碼,需要將它們放到磁盤的後續分區,然後被加載到內存中,才能使用。對於如上描述可以參考我博客的附件資源,其中包含了所有的實現,方便理解。

    另外推薦一份bios的參考手冊,絕對詳細:

http://www.cs.cmu.edu/~ralf/files.html

    關於中斷的參考手冊:

http://www.htl-steyr.ac.at/~morg/pcinfo/hardware/interrupts/inte1at0.htm


  二)C與彙編的互調

  在系統一上電運行的代碼肯定是彙編語言,等到將c語言運行環境創建好了之後才能運行之。所以需要了解如下幾個方面的知識:

   1.首先彙編代碼如何被執行

   根據上一節描述,我們寫的代碼會放在mbr裏,當然如果突破了mbr的限制,其他代碼可以放到硬盤的其他扇區。在mbr的代碼會被自動加載的0x7C00中執行,所以我們需要將我們的代碼鏈接到0x7C00,查看makefile會發現可以這樣實現:

   A.直接鏈接生成mbr


boot: boot.o  
	ld --oformat binary -N -Ttext 0x7c00 -o $@ $< #生成mbr扇區,鏈接地址爲0x7C00
   

  B.鏈接成elf文件後,再轉換:

 

read_sector.out:read_sector.o
	ld -N -Ttext 0x8400 -o [email protected] $< -e readsect -m elf_i386 #這裏是鏈接到地址0x8400,進入點爲readsect
	objcopy -S -O binary -j .text [email protected] $@
	#將生成read_sector.out.bak 轉換成 read_sector.out。

    方法2比方法1的優勢是:a.文件更小,b.生成的elf文件可以被反彙編分析。

  方法1的優勢:簡單。

   關於mbr的詳細介紹可以參考百度百科:

http://baike.baidu.com/link?url=sQhyg1wSqI1n3JgBuueAkh9NUlQ6iCG5a0HsfVexr4Ky3gwDfgxYBzsMwGZTcCzOqom8wfuxG62WxNYsP6WB6zOcdvocdfRtSUnKEkVC0Ji

  它的解析,有附件的一個c語言文件。獲取本機系統的mbr可以用如下命令:

sudo dd if=/dev/sda of=mbr bs=512 count=1

  2.彙編如何調用c語言:

  彙編爲了調用c語言,需要做兩步工作,第一創建c語言運行環境,第二調用c語言入口代碼。

   a)創建c運行環境:

   因爲c語言的基本結構是基於堆棧的,所以首先要創建堆棧:

init_stack:
        xorw %ax,%ax
        movw %ax,%es #設置堆棧段
        movw $0x7c00,%sp #設置棧頂
   c語言調用過程中肯定會讀寫全局數據,所以需要設置對應的數據段:

xorw %bp,%bp #清除bp,因爲它會被c語言用於棧幀的開始指向。
        movw %bp,%ds #設置數據段
        movw %bp,%es #設置擴展段
   b)調用c語言,傳入參數,然後調用對應的地址就可以了:

pushl $0 #設置第2個參數
      pushl $0x8000 #設置第一個參數
      calll 0x843b #調用c語言入口的函數地址
   注意傳遞參數的順序是,從右到左。

   3.c語言如何調用彙編語言:

   通過AT&T內嵌彙編語法調用:

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語言使用inline的方法,聲明函數爲內斂:

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.彙編語言編程的技術總結

      這裏講的彙編語言主要是unix標準下的AT&T語法,簡單來說與Intel的語法有差別的地方與特定的僞指令支持,爲什麼需要了解AT&T彙編呢?因爲在linux平臺上,甚至Unix下,都適用,它與硬件平臺無關,所以適用性很強。

  a.支持的指令格式,操作數與Intel彙編相反,比如:

  Intel :mov eax, ebx #ebx->(賦值給)eax

  AT&T  :mov %ebx,%eax

  編譯器會檢測操作順序,如果不能匹配,則出錯。

  b.Intel的指令,AT&T都支持,所以對於一些不瞭解的指令,可以參考Intel的相關技術手冊。

  c.操作數的表示格式

  寄存器前需要添加%

  立即數需要添加$

  默認常數爲地址

    d.對宏的支持:

.macro OUT_B v pa #定義宏——將v輸出到端口pa,參數爲v,pa

#通過宏定義,將參數v轉化爲簡單的表達,換句話說,在宏定義中,參數的引用爲\v。
#define V \v 
#define PA \pa
        movb $(V),%al #將V當作立即數使用
        movw $(PA),%dx
        outb %al,%dx
.endm

      f.運行數據的保存

     因爲intel只有8個寄存器EAX,EBX, ECX, EDX, ESI, EDIESPEBP可用,而且很多時候都是受限的——即特定的寄存器有特定的用途,比如:相對尋址只有用EBX。所以,寄存器的資源是不夠的,需要用堆棧來保存:



 xorw %ax,%ax
        movw %ax,%ds
        push %ds #保存ds
        movw $0x40,%ax
        movw %ax,%ds
        movw $0x47,%bx
        movb (%bx),%al
        pop %ds #恢復ds   

    g.對地址空間的使用:


                x: .byte 'x'
		y: .int 0x1234
		z: .word 0x2345
		str: .ascii “Hello world!\0”
		str1: .asciz “Hello world!”
		.的使用,表示當前地址
 

     如上的內容可以參考《gas_manual-unix彙編》與我附件的代碼。


    一葉說:終於寫完了,內容太多了,而且需要一步步謹慎的驗證;如上內容只是截取了主要的知識介紹,對我們後期理解系統的運行流程與實現相關代碼有幫助;請理解它們,請調試它們。如果需要更進一步瞭解相關內容,請參考詳實附件,這樣對理解整個系統與相關軟件的工作原理更有意義。最後還要感謝前輩們的知識總結,也希望後來的我們能夠站在它們的肩膀上,爬得更高。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章