彙編語言入門

1 本講座以彙編初學者或對彙編一點也不瞭解的讀者爲對象,彙編高手不屬於該範圍,但強烈建議高手指導並增補、修改本文。 2 任何讀者能夠跟此貼,提出疑問,或解答其中的問題,但對於全部跟貼,水貼、內容有錯、絕不相干貼將直接刪除,有意義的貼可能會合併到下一講的內容中,合併後也將刪除,請跟貼者諒解。同時按學習進步,請提問者逐步提,不要我沒開口,你就問怎麼編個病毒的問題。 3 藉以拋磚引玉,但不但願你們只朝我扔磚頭,但願你們踊躍思考,使之完善。 你們坐好了,。不要,不要,不要,男女同窗不要相互,女同窗不要對我…. 1 彙編須要什麼工具和程序,到哪裏下載? 目前階段,彙編程序僅須要兩個程序就夠了: masm.exe,link.exe點擊本站下載。前者是編譯程序,後者是連接程序。 另外,爲了驗證和調試程序,還須要一個程序debug.exe,該程序由windows自己就提供,因此就不提供下載地址了。 將兩者下載後,放到某一個目錄中(任意目錄均可以),考慮到不少命令須要經過鍵盤敲入,因此建議你不要把文件放入到長文件名目錄、中文目錄或很深的目錄中。好比你能夠建一個「D:\Masm」目錄,並建議此後的程序都放這個目錄,此後稱這個目錄爲彙編目錄。程序員

2 學習彙編須要有哪些編程方面的知識。 沒有任何編程方面的知識,學習此語言等於緣木求魚,因此請放棄學習的想法。通常來講至少要知道以下幾點: *)程序的運行邏輯結構有順序(按語句依次執行)、分支結構(IF…THEN…ELSE…),循環結構(FOR…NEXT)三種結構。 *)知道什麼是子程序,什麼是調用。 *)彙編程序員的視角。不一樣編程視角編程要求是不同的。好比刪除文件, >>用戶的視角是找到「刪除」按鈕或菜單,而後單擊一下便可。 >>高級程序員的視角是知道刪除的文件,併發出刪除命令。這些經過API實現。 >>彙編程員的視角是獲得要刪除的文件名,找到該文件所在位置,經過調用刪除「中斷命令」進行刪除。 >>操做系統開發人員的視角則是接到刪除命令後,先找到系統根目錄區,由根目錄區的連接依次找到子目錄區,直到找到要刪除的文件,而後按照操做系統刪除文件的規則對該文件名進行修改。好比DOS,只把第一個字符改爲"?"。算法

按程序語句等價的角度看,一行VB的打印語句,用匯編實現大約須要一百二十多行。知道彙編語言的視角後就要知道,前面的道路是坎坷的,沒有耐心是不行的。想經過幾分鐘幾行程序就完成很複雜的操做不是件容易的事。編程

3 學彙編有什麼用? 彙編產生於DOS時代或更早,而如今是Windows時代,因此可能遺憾地說:儘管還有批牛人在用匯編開發核心級程序,但咱們幾乎沒什麼用,除了必要時間能拿來分析一兩個程序的部分代碼以外,別的也就沒幹什麼用了。而且並非全部的彙編命令都能在windows下使用。而泛泛地追求「時髦」而學本語言,最後的結果是損了夫人又折兵。因此學以前你要考慮好。我勸那些爲了當「黑客」而學彙編的人就此止步。 第零講 預備知識windows

1 一個彙編程序的編譯過程是怎麼樣的。 1)首先你須要找一個編輯器,編輯器用任何「純文本」編輯器均可以。好比記事本。編好之後保存到彙編目錄中。擴展名爲asm,好比myfirst.asm。但這裏建議你找一個能顯示出當前行的編譯器。這樣出錯後排錯很容易。 2)而後在DOS下進入D:\Masm目錄中,輸入「masm myfirst.asm",若是有錯系統會提示出錯的行位置和出錯緣由。 3)而後再輸入「link myfirst.obj」,便可看到當前目錄下有一個myfirst.exe程序。併發

2 宏彙編和彙編有什麼區別嗎? 兩者的區別在於前者提供宏,後者不提供。後者已找不到了,因此你能夠認爲兩者沒有區別。框架

3 機器語言、彙編語言、高級語言的關係 最先的計算機採用機器語言,這種語言直接用二進制數表示,經過直接輸入二進制數,插拔電路板等實現,這種「編程」很容易出錯,每一個命令都是經過查命令表實現,既然是經過「查表」實現的,那固然也可讓計算機來代替人查表實現了。因而就產生了彙編語言,因此無論別人怎麼定義機、匯語言,我就認爲,兩者是等價。後來人們發現,用匯編語言編某一功能的時候,連續一段代碼都是相同或類似,因而就考慮用一句語言來代替這一段彙編語言,因而就產生了高級語言。所以,全部高級語言都能轉化成彙編語言,而因此彙編語言又可轉化成機器語言。反之,全部機器語言能夠轉成彙編語言(由於兩者等價)。但並非因此彙編語言都能轉成高級語言。編輯器

4 計算機的組成 一般都把計算機定義成五部分:運算器、控制器、存儲器、輸入系統、輸出系統。 爲了簡單其間,咱們如此理解:運算器+控制器=CPU。存儲器=內存(暫不包括外存,永不包括CACHE)。輸入系統=鍵盤(不包括鼠標),輸入系統=顯示器(不包括打印機,繪圖儀)。函數

5 寄存器和內存的區別 寄存器在CPU中。內存在內存條中。前者的速度比後者快100倍左右。後面的程序要求每條指定要麼沒有內存數據,要麼在有一個寄存器的參與下有一個內存數據。(也就是說,不存在只訪問內存的指令)。工具

6 彙編語言的計數 與生活中的計數不同,彙編中的計數是從0開始的。好比16個計數,則是從0~15,而不是生活中的1~16。這一點看起來簡單,真運算起來就不是件容易的事了,不信等着瞧。oop

7 進制問題 又與生活中不同的地方是進制。切記下面的常識: *)計算機內部存儲都用二進制。 *)咱們的彙編源程序默認都用十進制。(除非你指明類型) *)咱們用的調試程序debug默認的都是十六進制。(沒法指明其餘類型) 其中十六進制的十六個個位數依次是:0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F。

8 進制轉換 一個比較簡單的方法是查表法。 十進制 十六進制 二進制 0   0   0000   1   1   0001   2   2   0010   3   3   0011   4   4   0100   5   5   0101   6   6   0110   7   7   0111   8   8   1000   9   9   1001   10   A   1010   11   B   1011   12   C   1100   13   D   1101   14   E   1110   15   F   1111

好了,結合6,7,8三條。你們來算一個「題」。某一組數據顯示時,每一個數據佔了四個位置, 每行共十六個。問:十六進制的13位置在哪裏(第幾行,第幾列)。 格式以下:m m m m n n n n o o o o p p p p '注:之因此沒用ABC是怕與上面十六進制弄混。 r r r r s s s s t t t t u u u u

第一講 基礎知識

1 訪問內存 程序在內存中,訪問內存是幾乎每一程序都要進行的操做,計算機對內存編址是線性的,也就是說是一維的,好比256M的內存,地址就應該是從0~(256M-1),這個地址稱爲物理地址或絕對地址。 1.1 地址表示 但從彙編程序員的角度看,內存倒是二維的,要說明一個地址,須要給出兩個值,就象你在平面上指定一點須要說出(X,Y)座標同樣,彙編程序員的內存視角也須要兩個「座標」,前一個稱爲段地址(Segment),後一個稱爲偏移地址(Offset),該地址稱爲邏輯地址。 好比「1234:3DF5」就是一個地址。「1F3F:」不是一個地址,由於他只有段地址,沒有編移地址。注意此後的地址都用十六進制表示。 1.2 地址計算 前面提到,計算機編址是一維的,彙編程序員是二維的,那麼兩者怎麼換算呢?由後者到前者的換算方法是,「段地址串」後面加個「0」,而後再加上偏移地址。 好比「1234:3DF5」(十六進制的加減運算參見相關資料) 12340 ‘串後加了一個0 3DF5 —– 16135 ’注意此串仍然是十六進制。 因此,彙編程序員眼中的地址「1234:3DF5」就是物理地址(計算機編址):16135。 知道了由後者向前者的轉換,那麼由前者向後者的轉換呢? 「不知道」,爲何不知道,繼續往下看。 1.3 到底哪一個地址對。 知道了1.2的地址算法後,我又發現一個問題: 「1000:6135」的物理地址是多少呢? 10000+6135=16135。 「1001:6125」的物理地址呢? 10010+6125=16135。 …… 那麼到底哪一個對呢?問題的回答是這樣的:假設我如今讓你按一下「L」鍵,我能夠告訴你以下幾種方法中的一種或幾種。1 請按一下「L」鍵; 2請按一下鍵盤上第四行第十個鍵;3 請按一下第十列中的第四個鍵;4 請按一下「K」右邊的鍵;5 按標準指法單擊一下右手無名指。 舉上面的例子也就是說,同一個地址有不少種表示方式,具體用哪種,要看實際使用時的狀況。但不管用哪一種方式,只要能達到目的便可。(實際中該問題通常不會受此問題困擾,但初學時忽然想不通)。 1.4 有多少內存能夠訪問 不管是段地址仍是偏移地址都是四位十六進制(若是不夠四位,前面補0)。也就是說:總共能夠訪問的地址說是:0000:0000~FFFF:FFFF。 總共FFFF0+FFFF+1=10FFF0個地址。也就是不到1M的空間。 記住以下結論: *)無論你實際內存有多少,目前咱們只能訪問不到1M的空間。 *)而實際上連這1M也用不完。其中上端的384K的址只能讀不能寫,只能讀,通常稱爲ROM。 *)低端的640K能夠讀寫。但這640K的低端100多K也不能隨便寫,所以DOS系統使用該區。 *)原來1024M的內存,彙編程序只能使用其中400多K。這段內存的容易至關於一個普通文檔的大小。不過這就足夠了。

2 DEBUG的使用 先記住如下兩個命令:D命令和Q命令。前者是顯示內存內容,後者是退出DEBUG命令。 ————-如下爲抄別的人內容————— DEBUG.EXE程序是專門爲分析、研製和開發彙編語言程序而設計的一種調試工具,具備跟蹤程序執行、觀察中間運行結果、顯示和修改寄存器或存儲單元內容等多種功能。它能使程序設計人員或用戶觸及到機器內部,所以能夠說它是80X86CPU的心靈窗口,也是咱們學習彙編語言必須掌握的調試工具。

1)DEBUG程序使用

在DOS提示符下鍵入命令:

C>DEBUG [盤符:][路徑][文件名.EXE][參數1][參數2]

這時屏幕上出現DEBUG的提示符「-」,表示系統在DEBUG管理之下,此時能夠用DEBUG進行程序調試。若全部選項省略,僅把DEBUG裝入內存,可對當前內存中的內容進行調試,或者再用N和L命令,從指定盤上裝入要調試的程序;若命令行中有文件名,則DOS把DEBUG程序調入內存後,再由DEBUG將指定的文件名裝入內存。 2)DEBUG的經常使用命令 (1)退出命令 Q 格式:Q 功能:退出DEBUG,返回到操做系統。 (2)顯示存儲單元命令 D 格式1:D[起始地址] 格式2:D[起始地址][結束地址|字節數] 功能:格式1從起始地址開始按十六進制顯示80H個單元的內容,每行16個單元,共8行,每行右邊顯示16個單元的ASCII碼,不可顯示的ASCII碼則顯示「·」。格式2顯示指定範圍內存儲單元的內容,其餘顯示方式與格式1同樣。若是缺省起始地址或地址範圍,則從當前的地址開始按格式1顯示。 例如:   -D 200     ;表示從DS:0200H開始顯示128個單元內容 -D 100 120   ;表示顯示DS:0100-DS:0120單元的內容 說明:在DEBUG中,地址表示方式有以下形式: 段寄存器名:相對地址,如:DS:100 段基值:偏移地址(相對地址),如:23A0:1500

————————–小抄結束——————————–

3 驗證第一節裏的內容 運行「開始/程序/附件/MS-DOS命令提示符」(這是win2000,win98下本身找吧) 在「_」下輸入D,顯示 -d 1398:0100 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. 1398:0110 00 00 00 00 00 00 00 00-00 00 00 00 34 00 87 13 …………4… 1398:0120 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. 1398:0130 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. 1398:0140 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. 1398:0150 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. 1398:0160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. 1398:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. - 咱們記下:1398:011C的值是個34。1389:011C的物理地址應該是:13A9C。 那麼1000:3A9C的物理地址也應該是13A9C,他的內存也應該是34,(由於原本就是一個地址嗎,就象第三行第十列和第十列第三行固然應該是同一個位置)。 -d 1000:3A9C 1000:3A90                 34 00 87 13       4… 1000:3AA0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. 1000:3AB0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. 1000:3AC0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. 1000:3AD0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. 1000:3AE0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. 1000:3AF0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. 1000:3B00 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ……………. 1000:3B10 00 00 00 00 00 00 00 00-00 00 00 00       ………… - 果真如此,一樣你能夠驗證:13A9:000C也確定是指這一個地址,不信試試。 4 DEBUG命令 ——————-繼續小抄—————- 前面已學過:顯示存儲單元命令 D 再學一個命令 (1)修改存儲單元命令 E

格式1:E[起始地址] [內容表]

格式2:E[地址]

功能:格式1按內容表的內容修改從起始地址開始的多個存儲單元內容,即用內容表指定的內容來代替存儲單元當前內容。

例如:—E DS:0100 'VAR' 12 34

表示從DS:0100 爲起始單元的連續五個字節單元內容依次被修改成

'V'、'A'、'R'、12H、34H。

格式2是逐個修改指定地址單元的當前內容。

如:—E DS:0010

156F:0010 41.5F

其中156F:0010單元原來的值是41H,5FH爲輸入的修改值。若只修改一個單元的內容,這時按回車鍵便可;若還想繼續修改下一個單元內容,此時應按空格鍵,就顯示下一個單元的內容,需修改就鍵入新的內容,不修改再按空格跳過,如此重複直到修改完畢,按回車鍵返回DEBUG「-」提示符。若是在修改過程當中,將空格鍵換成按「-」鍵,則表示能夠修改前一個單元的內容。

——————-小抄結束—————-

5 使用DOS時,彙編用戶能夠從DOS操做系統中獲得什麼? 如今編程,一般不少功能都是經過調用系統API。不少高級語言都直接把這些API包裝起來,以系統接口或函數的方式提供給用戶,那麼彙編函數都能獲得什麼呢? 首先,彙編用戶有不少東西能夠調用。他們主要是: 5.1 BIOS提供的接口。如今硬件與軟件的區分已愈來愈不明顯,不少硬件不只僅是電路,而還要提供一些固化寫入硬件的一部分「程序」,這些程序以ROM的方式出現,彙編用戶最大的好處就是能夠直接使用這些「程序」,這些使用不只功能強大,並且效率很是高。 5.2 DOS功能調用,做爲操做系統也象BIOS同樣向用戶提供了相應的「程序」。這些程序在很大程序上擴充了BIOS。與BIOS不一樣的是,這部分程序放在內存中,它能夠被修改。而BIOS中不能再修改。 ========================================================== 以上兩種接口都經過一種相同的格式調用,這些程序統稱爲「中斷」,如今先不要理解中斷的本意,你如今能夠認爲是系統提供給你的函數。 ============================================================ 5.3 系統共享數據區。編過程序的人都知道全局變量的好處,全局變量方便以外在於任何函數、過程均可以調用、讀取、修改。全局變量不足之處是危險性,有一個過程改了這個變量值,其它的也得跟着改變了。DOS操做系統一樣也提供了這樣的共享數據區,該區是整個系統的共享區,任何程序均可以查找、修改。固然,修改某處必然會對其它程序形成影響。

6 再談中斷 前面5.2已提到中斷了,如今問題是不一樣硬件不同,即便相同硬件的ROM,不一樣版本,各個BIOS中斷程序所處的位置也不同,DOS中斷也同樣,不一樣版本、不一樣配置,在內存位置也不同。那麼你使用某一箇中斷,系統怎麼知道你使用的那個中斷程序在哪呢? 爲了解決這一問題,DOS會在啓動的時候,把全部這些(BIOS和DOS)中斷的首地址保存到一個地址。這個地址很容易記,這段地址是內存的絕對零地址(0000:0000)。前面已講過,每一個地址在彙編程序員角度來看是二維的,也就是分爲段地址和偏移地址。每一個地址各佔兩個字節,因此要表示這個二維地址須要4個字節。因此每一箇中斷首地址由4個字節表示。一共256箇中斷,佔用了1024個字節的位置。 另外須要注意的是,這4個表示地址的字節,數據是由低向高的。好比12 34 56 78所表示的地址是:7856:3412。 通常用INT M表示中斷M,若是M是十六進制,則在後面加上一個H。好比19號中斷,十六進制應該是13H。因此該中斷就是INT 13H。

7 再談系統共享數據區 該共享數據區在絕對地址:0040:0000開始。

8 驗證我上面說的內容 8.1 找中斷 運行DEBUG後。輸入D 0000:0000。顯示絕對零地址的內容。 C:\>debug -d 0:0 0000:0000 68 10 A7 00 8B 01 70 00-16 00 9B 03 8B 01 70 00 h…..p…….p. 0000:0010 8B 01 70 00 B9 06 0E 02-40 07 0E 02 FF 03 0E 02 ..p…..@……. 0000:0020 46 07 0E 02 0A 04 0E 02-3A 00 9B 03 54 00 9B 03 F…….:…T… 0000:0030 6E 00 9B 03 88 00 9B 03-A2 00 9B 03 FF 03 0E 02 n…………… 0000:0040 A9 08 0E 02 99 09 0E 02-9F 09 0E 02 5D 04 0E 02 …………]… 0000:0050 A5 09 0E 02 0D 02 DC 02-B8 09 0E 02 8B 05 0E 02 ……………. 0000:0060 02 0C 0E 02 08 0C 0E 02-13 0C 0E 02 AD 06 0E 02 ……………. 0000:0070 AD 06 0E 02 A4 F0 00 F0-37 05 0E 02 71 84 00 C0 ……..7…q… -u 0070:018B 0070:018B 1E       PUSH   DS 0070:018C 50       PUSH   AX 0070:018D B84000   MOV   AX,0040 0070:0190 8ED8     MOV   DS,AX 0070:0192 F70614030024 TEST   WORD PTR [0314],2400 0070:0198 754F     JNZ   01E9 0070:019A 55       PUSH   BP 0070:019B 8BEC     MOV   BP,SP 0070:019D 8B460A   MOV   AX,[BP+0A] 0070:01A0 5D       POP   BP 0070:01A1 A90001   TEST   AX,0100 0070:01A4 7543     JNZ   01E9 0070:01A6 A90002   TEST   AX,0200 0070:01A9 7422     JZ   01CD 首先,D命令把中斷首地址顯示出來。每4個表示一個地址。其中INT 0的中斷首地址爲:00A7:1068,INT 1的中斷地址爲:0070:018B…….0070:018B是中斷3的首地址。後面那個U命令就表示顯示該地址的「中斷程序」的內存。 大家能夠試着找找INT 13H的位置在哪。 8.2 驗證系統共享數據區 系統共享數據區內容極爲豐富,我實在記不住哪麼多了。我曾記在一個本上,惋惜那個本早在N年前(3<n<6)就丟了。兄弟們誰找到這個地址的內容,必定要貼上來,這裏有東西可讓你們眼界大開。 40:0="" p="" n<6)就丟了。兄弟們誰找到這個地址的內容,必定要貼上來,這裏有東西可讓你們眼界大開。<="" 0040:0000」的內容。你能夠找找。<="" 0040:0000」,則右邊顯示的是「d="" 第二次我輸入「d="" 40:0」,因此你能夠在此後顯示數據右邊字符中找到這些字符,注意是間隔開的。="" 既然是鍵盤緩衝區,每一個輸入的鍵都會顯示在該區中,第一次我只輸入了「d="" -="" …………….="" 01="" 00="" 14="" 00-14="" 0040:0070="" …..)0…..$…="" 11="" b8="" 24="" c0="" 7f-03="" 30="" 29="" 03="" d4="" 0c="" 0f="" 0040:0060="" 00-00="" 18="" 0040:0050="" ……….p…..="" 10="" 50="" e1-c8="" 09="" af="" a2="" c3="" 1f="" 0040:0040="" 0.0.0…..4.0…="" 0b="" 05="" 34="" 0e="" 0e-08="" 08="" 0040:0030="" 90.="" 0.0.0.0…d="" 39="" 20="" 64="" 1c="" 0b-0d="" 0040:0020="" "….(….*.*.:'="" 27="" 3a="" 2a="" 28="" 02="" 80="" c8="" 22="" 0040:0010="" ……….x.x…="" 9f="" 78="" 02-bc="" e8="" f8="" 0040:0000="" -d="" …..)0………="" b7="" a1="" ..="" 0.:'0…q…d="" 0d="" 1c-71="" 94.="" 4.0.:'0…d="" 9="" "….(….*.*.="" c:\>debug="" 其中每一個鍵有兩個字節,一個字節是ascii碼,一個是掃描碼。共16個。="" 在dos下,你每按一個鍵,系統都會記下來,下面咱們一塊兒找找這個鍵盤緩衝區的地址。知道這個地址,你就能夠做一個「虛擬」鍵盤,經過發命令來模擬某我的在按鍵。這個地址位於:0040:001e。="" 前幾年,我用的286計算機是黑白顯示器(555555~~~~~~~~~,別嫌我老、舊、慢呀),可當時有個遊戲非要彩顯,不是彩顯不讓運行。我就是改了這個區的某一個位,讓哪遊戲「覺得」我用的是彩顯,因而遊戲能用了。雖然很差看,但總能用。="">

第二講 內存映象

之因此把這個內存單獨放一章,是爲了說明它的重要性,後面的幾乎不少程序都須要你對這一章的理解。這裏的內存映象就是指當你把一個可執行文件(EXE或COM文件)放到內存後,整個內存「看」起來是什麼樣子的。 前面講過,這裏彙編程序只能訪問1M的內存空間,因此下面就以1M內存爲例。而且以DOS操做系統做爲講解對象,因此所編出來的程序也僅是DOS程序。事實上,經過winasm能夠訪問遠遠超過1M的空間,而且能夠編出FOR windows的程序。但那是另外的話題。咱們暫且不說那些。

2.1 內存映象 首先,這1M內存若是咱們再也不以二維的方式看,而是一維的,線性地看(二維和一維的轉化方式參見前面章節)。但描述仍是以二維的方式描述,從最底端到最高端依次是: 1 中斷向量區:該區由0000:0000~0000:03FF。這裏存着系統的全部中斷的中斷向量表,對於中斷向量表,你如今先理解爲一些程序的首地址。由這個地址你就能找到該程序。 2 系統數據區:該區由0040:0000~0040:XXXX(很差意思,忘了),這裏存着整個系統中,DOS操做系統要用的數據,因爲這個區的數據對用戶是開放的,因此用戶固然也能夠從這裏讀出來用。 3 DOS操做系統區:操做系統常駐內存,你向計算機發的每一個命令其實都是操做系統執行的。這個區的大小主要是由操做系統的版本和用戶的配置大小決定,若是是驅動程序配置,就放到根目錄下的config.sys裏,若是是程序,就放到autoexec.bat裏。這裏設置在如今的windows 95/98/nt/me/2000/xp/2003中仍然有,因此我就很少說了。 4 用戶程序,這個固然就是你執行的程序了,這種程序分兩種,一種是擴展名爲com文件,一種是exe文件,從程序內睝@矗罷叱絛虻乃母齠沃睾希ê竺嬉艙饉母齠危宰畲蟪ざ戎壞扔諞桓齠危們懊娑蔚刂返睦斫餼褪莄om文件最大隻能是64K,因此com文件只適合小的程序。而exe,四個段可任何分配,並可擴充段,並且每一個段的段地址能夠任何改動,所以exe的訪問內存能力大多了。這種格式訪問能力只受地址結構的限制了。 用戶程序所佔的內存大小徹底由程序自己決定,但最大,只能到640K。這一點,怪不得別人,只能怪當前計算機軟硬件設置高手高手高高手們(包括比爾蓋茨)們的失誤了,60年代的超級計算機只有36K的內存,因此他們就在80年代獲得一個結論:640K的內存足夠了。 若是用戶程序大於由操做系統所佔內存的頂底到640K之間的內存量,就會顯示:內存不夠,於是程序不能執行。這種現象對於一開始就用windows的人來講,幾乎沒見過,但對於一開始用DOS並打漢字的人來講,再正常不過。若是小於這段內存,多餘部分就空着。 5 從640K到1M-64K,這段內存就很難說清了。這段內存中有一部分被硬件佔有,有一部分是顯示緩衝區點有,還有一部分是系統ROM佔有。 6 從1M-64K到1M之間的這段64K的內存叫做HMA。這段內存是小孩沒娘,說來話長,咱們先不說他。 2.2 驗證上面的理論

2.2.1 中斷向量表 中斷向量表就是全部中斷向量首地址表,這裏保存着每一箇中斷程序的首地址,幾乎全部的彙編書都把中斷後面後面的章節中,而且對中斷的解釋也僅從字面意思解釋,因此致使大學對中斷的不重要和誤解。沒耐心的沒到這個章節就不學彙編了,有耐心的到這裏才豁然開朗。我如今不講中斷的原意。我直接告訴你,你把中斷當成API也許更合適。也就是說,別人把不少已做好的功能放到了內存中。而且把調用這一功能的號告訴了你,你只要調用這些功能號,系統就自動從這個中斷向量表中找到對應的中斷,而後執行你的功能。 首先讓你感覺一下中斷的魅力一下吧。好比中斷21H的2A功能調用是讀取系統的日期,這個調用的規則是,調用前AH寄存器置爲2A。調用後年在CX中,月在DH中,DL在日中,星期在AL中。 -a 139D:0100 mov ah,2a 139D:0102 int 21 139D:0104 int 3 139D:0105 -g=100

AX=2A05 BX=0000 CX=07D4 DX=0C18 SP=FFEE BP=0000 SI=0000 DI=0000 DS=139D ES=139D SS=139D CS=139D IP=0104 NV UP EI PL NZ NA PO NC 139D:0104 CC       INT   3 - 可能上面的程序你目前還看不懂。不過不要緊,「mov ah,2a」表示調用功能號是2a號。「int 21」表示調用十六進制21號中斷,「int 3」表示3號中斷,表示程序運行到這一句時停一下。「g=100」表示從「139D:0100 」開始執行。 AX=2A05 BX=0000 CX=07D4 DX=0C18 SP=FFEE BP=0000 SI=0000 DI=0000 DS=139D ES=139D SS=139D CS=139D IP=0104 NV UP EI PL NZ NA PO NC 表示執行的結果。其中CX是年,這個年是由CX中存。07D4十進制就是2004年。DH+DL=DX,因此DH=0C,DL=18。兩者轉化爲十進制就是DH=12,DL=24,也就是今天了。AX=AH+AL=2A05,因此AL=05。那就是今天是星期五。 上面可能大家如今還看不懂,不過經過解說你應該能夠知道,僅僅兩行命令,就讀到了如今的值。如今須要做的就是把這些值提取出來用做他用了。

從中斷的做來與中斷向量表又有什麼關係呢?原來你在彙編裏運行int 21時,系統就在上面的中斷向量表中找到int 21的中斷地址,該中斷的地址應該位於:0000:0084~0000:0087,具體算法前面已說明了。 -d 0000:0084 0087 0000:0080       7C 10 A7 00               |… - 找到內容是:00A7:107C。而後系統就轉到這個地址執行int 21。

2.2.2 系統數據區前面都已說明過。再也不多說。系統區,不少DOS中斷程序實現部分就在這個區。程序運行區依不一樣的程序而不用。

2.2.3 640K~1M之間,這期間有些地方是ROM,有些地方是硬件的BIOS區。我僅以兩個例子說明這一區。 ROM區:ROM區就是隻讀內存,也就是說這個區的數據只能讀不能寫。好比F000:0000開始的內存是ROM。咱們來寫一下,而後再看看效果。 -d f000:0000 0005 '顯示由F000:0000到F000:0005的六個字節值。 F000:0000 04 E8 A2 FF F9 C3               …… -e f000:0000   '修改命令 F000:0000 04.00 E8.00 A2.00 FF.00 F9.00 C3.00'注意,.後面的是我改的,把這幾個值都改爲0了。 -d f000:0000 0005 '再次顯示這個區的數據。 F000:0000 04 E8 A2 FF F9 C3               …… - 經過上面測試,發現該區數據仍然未改變。但你要是試別的RAM區的,確定會變。若是想試你本身試試吧。

顯示緩衝區:在文本方式下,B800:0000開始的地址保存着屏幕上每一個字符位置的值。在文本方式下,屏幕被分爲80 X 25。每一個位置有兩個值,一個值是ASCII字符,一個值是該ASCII的屬性值(主要是顏色)。因此一個屏幕共有80X25X2=400個字符。 咱們來改: -d b800:0000 0010 '顯示屏幕緩衝區的內容,注意此時本行最左邊的「-」是屏幕左上角。 B800:0000 2D 07 64 07 20 07 62 07-38 07 30 07 30 07 3A 07 -.d. .b.8.0.0.:. B800:0010 30                       0 - 看上面的命令,屏幕最上邊一行是「-d b800:0000 0010」,因此他的內容就是「2D 07 64 07 20 07 62 07-38 07 30 07 30 07 3A 07」其中,2D是「-」的ASCII值,07是「-」的屬性值。64是「d」的ASCII值,07是「d」的屬性值。。。。。 如今修改這些值。我把左上角的字改爲黃顏色的「-」,那固然是改b800:0001的屬性值了。 -e b800:0001 0e 是否是左上角的顏色變成黃色了嗎? 好了,把第二個字符變成綠色的「-」吧? -e b800:0002 2d 0b 變了嗎?

第三章 彙編指令 3.1 什麼是機器語言 前面提到「最先的計算機採用機器語言,這種語言直接用二進制數表示,經過直接輸入二進制數,插拔電路板等實現,這種「編程」很容易出錯,每一個命令都是經過查命令表實現」。 好比要執行21號中斷,須要查表,獲得21號中斷的指令就是CD 21。這樣無論你經過什麼方式,在內存指令位置,寫入兩個字節,一個是CD(這可不是音樂光盤,而是二進制數,轉成十進制就是205),另外一個是21(一樣是十六進制,十進制是33)。 上面就是機器語言。

3.2 什麼是彙編語言 前面也提到「既然是經過「查表」實現的,那固然也可讓計算機來代替人查表實現了。因而就產生了彙編語言」,彙編語言產生的重要目的就是用容易記的符號來代替容易出錯的二進制數(或十六進制數)。 好比前面的21號中斷,機器語言是CD 21。而彙編語言就規定中斷用int表示(interrupt的前三個字母),21號中斷就成了int 21h。其中21後面的h表示是表示這個21是十六進制。因爲大小寫不敏感,因此int 21h寫成下列方式都等價: int 33 Int 21h INT 21H

3.3 彙編指令集 1、數據傳輸指令 ─────────────────────────────────────── 它們在存貯器和寄存器、寄存器和輸入輸出端口之間傳送數據. 1. 通用數據傳送指令. MOV 傳送字或字節. MOVSX 先符號擴展,再傳送. MOVZX 先零擴展,再傳送. PUSH 把字壓入堆棧. POP 把字彈出堆棧. PUSHA 把AX,CX,DX,BX,SP,BP,SI,DI依次壓入堆棧. POPA 把DI,SI,BP,SP,BX,DX,CX,AX依次彈出堆棧. PUSHAD 把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次壓入堆棧. POPAD 把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次彈出堆棧. BSWAP 交換32位寄存器裏字節的順序 XCHG 交換字或字節.( 至少有一個操做數爲寄存器,段寄存器不可做爲操做數) CMPXCHG 比較並交換操做數.( 第二個操做數必須爲累加器AL/AX/EAX ) XADD 先交換再累加.( 結果在第一個操做數裏 ) XLAT 字節查錶轉換. ── BX 指向一張 256 字節的表的起點, AL 爲表的索引值 (0-255,即 0-FFH); 返回 AL 爲查表結果. ( [BX+AL]->AL ) 2. 輸入輸出端口傳送指令. IN I/O端口輸入. ( 語法: IN 累加器, {端口號│DX} ) OUT I/O端口輸出. ( 語法: OUT {端口號│DX},累加器 ) 輸入輸出端口由當即方式指定時, 其範圍是 0-255; 由寄存器 DX 指定時, 其範圍是 0-65535. 3. 目的地址傳送指令. LEA 裝入有效地址. 例: LEA DX,string ;把偏移地址存到DX. LDS 傳送目標指針,把指針內容裝入DS. 例: LDS SI,string ;把段地址:偏移地址存到DS:SI. LES 傳送目標指針,把指針內容裝入ES. 例: LES DI,string ;把段地址:偏移地址存到ES:DI. LFS 傳送目標指針,把指針內容裝入FS. 例: LFS DI,string ;把段地址:偏移地址存到FS:DI. LGS 傳送目標指針,把指針內容裝入GS. 例: LGS DI,string ;把段地址:偏移地址存到GS:DI. LSS 傳送目標指針,把指針內容裝入SS. 例: LSS DI,string ;把段地址:偏移地址存到SS:DI. 4. 標誌傳送指令. LAHF 標誌寄存器傳送,把標誌裝入AH. SAHF 標誌寄存器傳送,把AH內容裝入標誌寄存器. PUSHF 標誌入棧. POPF 標誌出棧. PUSHD 32位標誌入棧. POPD 32位標誌出棧.

2、算術運算指令 ─────────────────────────────────────── ADD 加法. ADC 帶進位加法. INC 加 1. AAA 加法的ASCII碼調整. DAA 加法的十進制調整. SUB 減法. SBB 帶借位減法. DEC 減 1. NEC 求反(以 0 減之). CMP 比較.(兩操做數做減法,僅修改標誌位,不回送結果). AAS 減法的ASCII碼調整. DAS 減法的十進制調整. MUL 無符號乘法. IMUL 整數乘法. 以上兩條,結果回送AH和AL(字節運算),或DX和AX(字運算), AAM 乘法的ASCII碼調整. DIV 無符號除法. IDIV 整數除法. 以上兩條,結果回送: 商回送AL,餘數回送AH, (字節運算); 或 商回送AX,餘數回送DX, (字運算). AAD 除法的ASCII碼調整. CBW 字節轉換爲字. (把AL中字節的符號擴展到AH中去) CWD 字轉換爲雙字. (把AX中的字的符號擴展到DX中去) CWDE 字轉換爲雙字. (把AX中的字符號擴展到EAX中去) CDQ 雙字擴展. (把EAX中的字的符號擴展到EDX中去)

3、邏輯運算指令 ─────────────────────────────────────── AND 與運算. or 或運算. XOR 異或運算. NOT 取反. TEST 測試.(兩操做數做與運算,僅修改標誌位,不回送結果). SHL 邏輯左移. SAL 算術左移.(=SHL) SHR 邏輯右移. SAR 算術右移.(=SHR) ROL 循環左移. ROR 循環右移. RCL 經過進位的循環左移. RCR 經過進位的循環右移. 以上八種移位指令,其移位次數可達255次. 移位一次時, 可直接用操做碼. 如 SHL AX,1. 移位>1次時, 則由寄存器CL給出移位次數. 如 MOV CL,04 SHL AX,CL

4、串指令 ─────────────────────────────────────── DS:SI 源串段寄存器 :源串變址. ES:DI 目標串段寄存器:目標串變址. CX 重複次數計數器. AL/AX 掃描值. D標誌 0表示重複操做中SI和DI應自動增量; 1表示應自動減量. Z標誌 用來控制掃描或比較操做的結束. MOVS 串傳送. ( MOVSB 傳送字符. MOVSW 傳送字. MOVSD 傳送雙字. ) CMPS 串比較. ( CMPSB 比較字符. CMPSW 比較字. ) SCAS 串掃描. 把AL或AX的內容與目標串做比較,比較結果反映在標誌位. LODS 裝入串. 把源串中的元素(字或字節)逐一裝入AL或AX中. ( LODSB 傳送字符. LODSW 傳送字. LODSD 傳送雙字. ) STOS 保存串. 是LODS的逆過程. REP 當CX/ECX<>0時重複. REPE/REPZ 當ZF=1或比較結果相等,且CX/ECX<>0時重複. REPNE/REPNZ 當ZF=0或比較結果不相等,且CX/ECX<>0時重複. REPC 當CF=1且CX/ECX<>0時重複. REPNC 當CF=0且CX/ECX<>0時重複.

5、程序轉移指令 ─────────────────────────────────────── 1>無條件轉移指令 (長轉移) JMP 無條件轉移指令 CALL 過程調用 RET/RETF過程返回. 2>條件轉移指令 (短轉移,-128到+127的距離內) ( 當且僅當(SF XOR OF)=1時,OP1 JA/JNBE 不小於或不等於時轉移. JAE/JNB 大於或等於轉移. JB/JNAE 小於轉移. JBE/JNA 小於或等於轉移. 以上四條,測試無符號整數運算的結果(標誌C和Z). JG/JNLE 大於轉移. JGE/JNL 大於或等於轉移. JL/JNGE 小於轉移. JLE/JNG 小於或等於轉移. 以上四條,測試帶符號整數運算的結果(標誌S,O和Z). JE/JZ 等於轉移. JNE/JNZ 不等於時轉移. JC 有進位時轉移. JNC 無進位時轉移. JNO 不溢出時轉移. JNP/JPO 奇偶性爲奇數時轉移. JNS 符號位爲 "0" 時轉移. JO 溢出轉移. JP/JPE 奇偶性爲偶數時轉移. JS 符號位爲 "1" 時轉移. 3>循環控制指令(短轉移) LOOP CX不爲零時循環. LOOPE/LOOPZ CX不爲零且標誌Z=1時循環. LOOPNE/LOOPNZ CX不爲零且標誌Z=0時循環. JCXZ CX爲零時轉移. JECXZ ECX爲零時轉移. 4>中斷指令 INT 中斷指令 INTO 溢出中斷 IRET 中斷返回 5>處理器控制指令 HLT 處理器暫停, 直到出現中斷或復位信號才繼續. WAIT 當芯片引線TEST爲高電平時使CPU進入等待狀態. ESC 轉換到外處理器. LOCK 封鎖總線. NOP 空操做. STC 置進位標誌位. CLC 清進位標誌位. CMC 進位標誌取反. STD 置方向標誌位. CLD 清方向標誌位. STI 置中斷容許位. CLI 清中斷容許位.

6、僞指令 ─────────────────────────────────────── DW 定義字(2字節). PROC 定義過程. ENDP 過程結束. SEGMENT 定義段. ASSUME 創建段寄存器尋址. ENDS 段結束. END 程序結束. 3.4 再談寄存器和內存的區別 第零講說到「寄存器在CPU中。內存在內存條中。前者的速度比後者快100倍左右。後面的程序要求每條指定要麼沒有內存數據,要麼在有一個寄存器的參與下有一個內存數據。(也就是說,不存在只訪問內存的指令)。」 寄存器是在CPU中的存儲器,而內存是在內存條中的存儲器。CPU訪問寄存器,只須要經過微指令直接就能夠訪問,而訪問內存則要先通過總線,再由總線到達內存控制器,讀到某單元的內存數據後放上總線,再傳到CPU中,CPU才能使用。 8086系列計算機的寄存器,共有14個,每一個都是十六位的。 AX,BX,CX,DX,SP,BP,SI,DI,CS,DS,SS,ES,IP,FLAGS。 其中前四位,每一個能夠單位再分紅兩個,AX=AH+AL,BX=BH+BL,CX=CH+CL,DX=DH+DL。這些分開的每一個都是8位的。 這個分開不要理解成平時語言中的分開,你能夠理解爲AX是由AH和AL組合成的,你給AL付值,就意味着同時給AX的低半部付值。你給AX付值,就意味着同時改變AH和AL。這樣做的好處是你能夠更靈活地控制這個寄存器。

3.5 指令說明 看了3.3的指令集和3.4的寄存器,是否是已經了,或者了?不要急,上面的東西雖然多,我也沒讓你一下學會,(其實有些永遠也不會彷佛也不是什麼大不了的事)。爲了應付看的懂我後面所說的,我把其中的指令挑幾個重點的,你必需要記住,其它的慢慢學吧。

1數據傳輸指令。 mov A,B 注意不是move,這個指令是把B中的數據複製給A,(B中仍保存原狀)。這裏的A和B能夠是寄存器,能夠是內存。但能夠同時是寄存器,不能同時是內存。好比 mov ax,100 ;這是對的,注意100在這裏叫當即數,但這個數在編譯系統編譯成exe的時候保存在內存中。若是學過別的高級語言,你就能夠理解爲這就是賦值語句 Let ax=100/ax:=100;/ax=100。 2 僞指令 僞指令就是否是真的指令,但他同時又是指令。之因此說這樣矛盾的話,是由於僞指令不是機器語言的一部分,而是彙編語言的一部分,是你告訴彙編的編譯系統如何去做。 string DB '這是個人第一個彙編語言程序$' 上面一行指令中,DB就是僞指令,他的做用就是告訴編譯程序,把後面一些數據或字符串放到內存中。固然對於exe來講,已在內存中了,就不用「告訴」了。(這就是爲何叫僞指令)。string是你給這段內存起的名字,若是你不須要這段內存,不起名字也能夠,但若是後面要用,固然要加上這個名字。'這是個人第一個彙編語言程序$'這個就是要處理的數據,固然你也能夠換成別的內容,但須要注意的是,要以'$'結尾,這是彙編的約寫,即:只是到了$,就認爲字符串結束,不然就一直向下找,直到找到一個$爲止。因此這就要求你的字符串中不能有'$',若是必須有,再換別的處理方式,後面再說。 3 地址傳送指令 Lea A,string 前面已經定義了string,後面要把地址找到,就要用到lea指令。lea是把字符串的地址給A這個寄存器中,A固然能夠上前面提到的任意寄存器。注意地址和內容的區別。若是是內容就是把string的字符串給A了。(固然這也不成立,一個字符串有不少字節,而一個寄存器只有兩個字節)。 那麼從上面也看到了,string表明一個地址,lea把這個地址給了A,那這個地址到底在哪裏呢?事實上這不重要,就象你要把某書店買書,這個書店在哪並非最重要的,有沒有你要的書纔是最重要的。因此你前面標出string,後面引用就好了,至於這個地址到底在哪是編譯程序的事,不是你的事。

4 運算指令 ADD A,N 這個很容易理解吧,寄存器A加上N,把和仍存在A中。相似於高級語言中的let a=a+n/a:=a+n/a+=n。

5 串操做指令 記住串操做指令表面很複雜,其實很簡單。 由於他就象一個複雜的數學公式同樣簡單,你所要記住的就是公式的格式,使用時具體套用便可。 從一個地址到另外一個地址的複製須要注意的是: *把源串段地址給DS。 *把源串編址給SI。 *把目的串段址給ES。 *把目的串偏址給DI。 *把要複製的個數給CX,這裏可不考慮$了。 *把FLAG中的方向標誌標誌你要的方向,一個是順向,另外一個是逆向。 *發送loop movs,scans等命令。

6 轉移指令 記住:無條件轉移指令 jmp。等於轉 jz,不等於時轉jnz

7 中斷指令 int 中斷號,注意進制,默認是十進制,因此十六進制就加h。

好了,上面的指令變成七八個了,這你不能嫌多了吧,若是再嫌多就不要繼續向下看了。

4.1 彙編程序框架

data SEGMENT '數據段,編程者能夠把數據都放到這個段裏 ….數據部分 '數據格式是: 標識符 db/dw 數據。 data ENDS'數據段結束處。

edata SEGMENT '附加數據段,編程者能夠把數據都放到這個段裏 ….附加數據部分 edata ENDS'附加數據段結束處。

code SEGMENT'代碼段,實際的程序都是放這個段裏。 ASSUME CS:code,DS:data,ES:edata '告訴編譯程序,data段是數據段DS,code段是代碼段CS start:MOV AX,data '前面的start表示一個標識位,後面用到該位,若是用不到,就能夠不加 MOV DS,AX '這一句與上一行共同組成把data賦值給DS。段寄存器. MOV AX,edata   MOV ES,AX '與前一句共同組成edata->ES …….程序部分 MOV AX,4C00h'程序退出,該句內存由下一行決定。退出時,要求ah必須是4c。 INT 21h code ENDS'代碼段結束。 END start'整個程序結束,而且程序執行時由start那個位置開始執行。

上面就是一個程序的框架結構。在這個結構中,有三個段,DS,ES,CS。這三個段分別存數據,附加數據,代碼段。

4.2 編寫咱們的Hello,world思路。 開始編寫咱們的第一個程序。 程序要求:顯示一個「Hello,Mr.286.」怎麼樣? 思路: 1 要顯示一個字符串,根據前面我讓大家記的七八個指令夠嗎?答案是:不只夠,並且還用不完。 首先定義一下總能夠吧。

hellostr db 'Hello,Mr.286.$' 最後的$不要忘了。

2 首先要考慮的問題就是找中斷,找到合適的中斷,該中斷就能幫咱們完成這個顯示任務。我找到(在哪找到的,怎麼找到的,別問我,到網上或書上都能找到): ——————————————- 中斷INT 21H功能09H

功能描述: 輸出一個字符串到標準輸出設備上。若是輸出操做被重定向,那麼,將沒法判斷磁盤已滿 入口參數: AH=09H DS:DX=待輸出字符的地址 說明:待顯示的字符串以’$’做爲其結束標誌 出口參數: 無 ——————————————- 由上面看到,咱們所須要做的就是把DS指向數據段,DX指向字符串的地址,AH等於9H,調用21h中斷。 mov ds,數據段地址 lea dx,hellostr 'hellostr已在前面1中定義了。 mov ah,9h int 21h。 因爲只要在調用int 21h以前把準備的東西準備齊就好了,因此int 21h前面三行的順序並不重要。

3 退出程序,運行完總要退出呀。再查中斷手冊 ——————————————– 中斷INT 21H功能4CH

功能描述: 終止程序的執行,並可返回一個代碼 入口參數: AH=4CH AL=返回的代碼 出口參數: 無

——————————————– mov ah,4Ch mov al,0 int 21h 或 mov ax,4c00h int 21h 這裏須要說明的是返回代碼有什麼用,返回給誰?返回給操做系統,由於是操做系統DOS調用的這個程序,這個返回值能夠經過批處理中的errorlevel獲得,這裏很少說明,實際上操做系統不多處理這一值,所以al你隨便寫什麼值影響都不大。

4.3 程序實現 data SEGMENT msg DB 'Hello, Mr.286.$' data ENDS

code SEGMENT   ASSUME CS:code,DS:data start:MOV AX,data   MOV DS,AX   lea dx,msg   mov ah,9h   int 21h   MOV AX,4C00h   INT 21h code ENDS END start

4.4 編譯運行。 把上面程序保存成hello286.asm後,就能夠編譯運行了。進入DOS,進入彙編目錄,若是還沒下載,到前面找下載地址。

================================================= E:\Download\Masm>masm hello286.asm Microsoft (R) Macro Assembler Version 5.00 Copyright (C) Microsoft Corp 1981-1985, 1987. All rights reserved.

Object filename [hello286.OBJ]: Source listing [NUL.LST]: Cross-reference [NUL.CRF]:

50408 + 415320 Bytes symbol space free

  0 Warning Errors   0 Severe Errors 說明:上面連續三個回車,表示我要的都是默認值。下面是零個警告,零個嚴重錯誤,(固然了,個人程序還敢錯嗎?)

E:\Download\Masm>link hello286

Microsoft (R) Overlay Linker Version 3.60 Copyright (C) Microsoft Corp 1983-1987. All rights reserved.

Run File [HELLO286.EXE]: List File [NUL.MAP]: Libraries [.LIB]: LINK : warning L4021: no stack segment

說明:三個回車仍要默認,後面有個警告,沒有棧段,這個不要緊,沒有的話系統會自動給一個()。

E:\Download\Masm>hello286 Hello, Mr.286. 說明:運行成功。 E:\Download\Masm> =================================================== 4.4 深度思考 4.4.1 是否是數據必須放數據段,代碼必段放代碼段呢? 答,代碼必段放代碼段,不然你怎麼執行呀?但數據也能夠放到代碼段,只是程序要做修改。 code SEGMENT   ASSUME CS:code,DS:data   msg DB 'Hello, Mr.286.$' start:MOV AX,data   MOV DS,AX   lea dx,msg   mov ah,9h   int 21h   MOV AX,4C00h   INT 21h code ENDS END start 編譯後仍然能夠。 4.4.2 我編的程序在內存中是什麼樣子的呢? ———————————————————————— E:\Download\Masm>debug hello286.exe -u 1420:0000 B81F14   MOV   AX,141F 1420:0003 8ED8     MOV   DS,AX 1420:0005 8D160000   LEA   DX,[0000] 1420:0009 B409     MOV   AH,09 1420:000B CD21     INT   21 1420:000D B8004C   MOV   AX,4C00 1420:0010 CD21     INT   21 1420:0012 FF362421   PUSH   [2124] 1420:0016 E87763   CALL   6390 1420:0019 83C406   ADD   SP,+06 1420:001C FF362421   PUSH   [2124] -d 141f:0000 L20 141F:0000 48 65 6C 6C 6F 2C 20 4D-72 2E 32 38 36 2E 24 00 Hello, Mr.286.$. 141F:0010 B8 1F 14 8E D8 8D 16 00-00 B4 09 CD 21 B8 00 4C …………!..L -q

E:\Download\Masm> —————————————————————————— 上面是什麼呀?,還記得前面說的嗎? 1420:0000 B81F14   MOV   AX,141F | |   |     |     | 段址:偏址 機器語言   mov指令 把段地址的地址(141f)賦值給AX寄存器。

1420:0012後面的是垃圾數據,不用管它,把上面程序與源程序做一個比較,看有什麼不用,差異在於把標號語言轉成實際地址了。 程序前兩行一執行,數據段地址就變成了141f,而那個字符串偏移地址在0000,由(LEA   DX,[0000]看出),因此我用-d 141f:0000 L20(後面L20表示只顯示20個字節),就能把段地址顯示出來了。 因此剛纔的程序在內存中就變成了: 141f:0000 Hello, Mr.286.$ —–>這是段地址裏的內存 1420:0000 B81F14   MOV   AX,141F ——>這是代碼段裏的內存。data變成了實際地址 1420:0003 8ED8     MOV   DS,AX 1420:0005 8D160000   LEA   DX,[0000] ——>偏址變成了0000,由於實際上msg也就是從頭開始的。固然是0了。 1420:0009 B409     MOV   AH,09   ——->注意Debug裏,默認的是十六進制 1420:000B CD21     INT   21 1420:000D B8004C   MOV   AX,4C00 1420:0010 CD21     INT   21 剛剛學習了8086/8088彙編語言,發現尋址方式很是重要,因而作了一個小總結,請各位笑納。 概念: 1.指令集:cpu可以執行的指令的集合。 2.指令:cpu所可以執行的操做。 3.操做數:參加指令運算的數據。 4.尋址方式:在指令中獲得操做數的方式。 如今就重點討論尋址方式,說白了也就是cpu怎麼樣從指令中獲得操做數的問題。另外再強調一點操做數還分種類: 1)數據操做數:全都是在指令當中參加操做的數據。 1.當即操做數:它在指令中直接給出。 2.寄存器操做數:它被放到寄存器中。 3.存儲器操做數:固然在存儲器也就是內存中。 4.i/o操做數:它在你給出的i/o端口中。 2)轉移地址操做數:在指令當中不是參加運算或被處理的數據了,而是轉移地址。 還能夠按照下面分類方式: 1)源操做數src 2)目的操做數dst 源操做數都是指令當中的第2個操做數,在執行完指令後操做數不變。而目的操做數是指令當中的第1個操做數,在執行完操做指令後被新的數據替代。 咱們就圍繞這幾種操做數,也就是操做數所在的位置展開討論。 先說數據操做數,它分3大類共7種。 1)當即數尋址方式:是針對當即操做數的尋址方式。在指令當中直接給出,它根本就不用尋址。 例1:mov ax,1234h mov [bx],5678h 在這裏1234h和5678h都是當即操做數,在指令當中直接給出。 2)寄存器尋址方式:是針對寄存器操做數的尋址方式,它在寄存器中咱們就用這中方式來找到它。 例2:mov bx,ax mov bp,[si] 在這裏ax,bx,ds都算是寄存器尋址,例1中的ax也是寄存器尋址方式。 3)存儲器尋址方式:針對在內存中的數據(存儲器操做數)都用這種方式來尋找,一共有5種(這是我本身的說法,便於記憶)。 不得不說起如下的概念:因爲8086/8088的字長是16bit,可以直接尋址2的16次方也就是64kb,而地址總線是20bit,可以直接尋址2的20次方也就是1M空間,因此把內存分爲若干個段,每一個段最小16byte(被稱爲小節),最大64kb,它們之間能夠相互重疊,這樣一來內存就被分紅以16byte爲單元的64k小節,cpu就以1小節爲單位尋址:在段寄存器中給出段地址(16bit),在指令當中給出段內偏移地址(16bit),而後把段地址左移4bit再與偏移地址求和就獲得數據在內存當中的實際物理地址了,於是能夠找到數據。 1.存儲器直接尋址方式:在指令當中以 [地址] 的方式直接給出數據所在內存段的偏移地址。 例3:mov ax,es:[1234h] mov dx,VALUE mov dx,[VALUE] 在這裏[1234h]和VALUE就是在指令中直接給出的數據所在內存段的偏移地址(16bit)。 VALUE是符號地址,是用僞指令來定義的,它表明一個在內存中的數據(也就是它的名字)。es:是段前綴符,用來指出段地址,在這以前應該將段地址添入段中,本例中是es,默認是ds,也就是不需給出。應該注意 [地址] 與當即尋址的區別,在直接給出的數據兩邊加 [] 表示存儲器直接尋址,以區別當即尋址。另外 VALUE=[VALUE]。 2.寄存器間接尋址:不是在指令中直接給出數據在內存中的偏移地址,而是把偏移地址放到了寄存器中。 例4:mov ax,[bx] 這裏[bx]就是寄存器間接尋址,bx中應方入段內偏移地址。其中:若使用bx,si,di默認段地址爲ds,若使用bp則默認段地址爲ss,而且容許段跨越,也就是加段前綴符。注意:在寄存器兩邊加 [] 以與寄存器尋址區別。 3.寄存器間接相對尋址:偏移地址是bx,bp,si,di中的內容再與一個8bit或16bit 的位移量之和。 例5:mov ax,[bx]+12h mov ax,[si]+5678h mov ax,[bp]+1234h 在這裏[bx]+12h,[si]+5678h,[bp]+1234h都是寄存器間接相對尋址。12h是8bit位移量,1234h和5678h是16bit位移量。若使用bx,si,di則默認段寄存器是ds,若使用bp則默認段寄存器是ss,而且容許段跨越。 4.基址變址尋址:偏移地址是一個基址寄存器和一個變址寄存器內容的和,既:bx或bp中的一個與si或di中的一個求和而獲得。 例6:mov ax,[bx+si] mov ax,[bp+di] 上面[bx+si]和[bp+di]都是基址變址尋址。若使用bx作基址寄存器則默認段地址爲ds,若使用bp爲基址寄存器則默認段爲ss,容許段跨越。 5.基址變址相對尋址:偏移量是一個基址寄存器一個變址寄存器只和再與一個8bit或一個16bit位移量只和獲得。 例7:mov ax,[bx+si]+12h mov ax,[bp+di]+1234h [bx+si]+12h和[bp+di]+1234h就是基址變址相對尋址。若使用bx作基址寄存器則默認段是ds,若使用bp作基址寄存器則默認段爲ss。容許段跨越。

下面是轉移地址操做數的尋址方式: 1)段內直接轉移 1.段內直接短轉移:cs(代碼段)內容不變,而ip(指令指針寄存器)內容由當前ip內容+(-127~127),在指令中直接給出。 例8:jmp short SHORT_NEW_ADDR 其中,short是段內短轉移的操做符,用以指出是轉移到當前位置先後不超過±127字節的地方。而NEW_ADDR是要轉移到的符號地址,它的位置應該在當前ip指針所在偏移地址不超過 ±127的地方。不然語法出錯。 2.段內直接近轉移:cs內容不變,而ip內容由當前ip內容+(-32767~32767),在指令中直接給出。 例9:jmp near ptr NEAR_NEW_ADDR 其中near ptr是段內近轉移的操做符,用以指出轉移到當前位置先後不超過±32767的地方。NEAR_NEW_ADDR是要轉移到的符號地址。 2)段內間接轉移:cs的內容不變,而ip的內容放在寄存器中或者存儲器中給出。 例10:jmp bx   jmp word ptr [bx]+1234h 這種尋址方式是在寄存器或存儲器中找到要轉移到的地址,而地址是16bit的,於是寄存器必須爲16bit,如:bx,咱們用word ptr來指定存儲器單元也是16bit的。注意:它是間接的給出,只能使用相似於數據操做數中的除當即尋址之外的6種尋址方式(就在上面)。 3)段間直接尋址:cs和ip的內容全都變化,由指令當中直接給出要轉移到的某一個段內的某一個偏移地址處。 例11:jmp 1234h:5678h   jmp far ptr NEW_ADDR 1234h送入cs中做爲新的段地址,5678h送入ip中做爲新的偏移地址。far ptr是段間直接轉移操做符,NEW_ADDR是另一個段內的偏移地址,在這個指令中把NEW_ADDR的段地址送入cs(不用你給出),把它的段內偏移地址送入ip中做爲新的偏移地址。 4)段間間接尋址:cs和ip的內容全變化,由指令當中給出的一個4字節連續存儲單元,其中低2字節送入ip做爲偏移地址,高2字節送入cs做爲段地址。 例12:jmp dword ptr [bx][si]+1234h   jmp dword ptr [1234h]   jmp dword ptr [si] dword ptr是雙字(4個字節連續存儲單元)操做符,用來指出下面的存儲單元是4個字節的。因爲它是4個字節的,因此只能使用相似於數據操做數中的存儲器尋址方式(共5種,還記得嗎?)。

另外做爲特殊的尋址方式還有三種:I/O尋址,串尋址,隱含尋址。它們都分別針對I/O指令,串操做指令以及無操做數的指令,並且都比較簡單,讀者自行總結。

到此爲止說明了8086/8088cpu中的全部尋址方式,我這裏只是個總結,具體的細節還要你們本身鑽研課本,才能理解。 寫的有些倉促可能有些遺漏或錯誤,還請諒解。

相關文章
相關標籤/搜索