彙編基礎入門知識

最近項目須要彙編,以前徹底沒接觸過,百度到這篇文章,就從它開始學吧!html

轉載過來留存:http://www.blogjava.net/wxqxs/archive/2009/09/17/277328.htmljava

原地址:http://www.zxbc.cn/html/20070611/22772.html (貌似打不開了。。。。)程序員

 

學習彙編前你應該知道的知識
算法

一、彙編須要什麼工具和程序,到哪裏下載?編程

目前階段,彙編程序僅須要兩個程序就夠了。masm.exe,link.exe。 前者是編譯程序,後者是連接程序。另外,爲了驗證和調試程序,還須要一個程序debug.exe,該程序由windows自己就提供。
將兩者下載後,放到某一個目錄中(任意目錄均可以),考慮到不少命令須要經過鍵盤敲入,因此建議你不要把文件放入到長文件名目錄、中文目錄或很深的目錄中。好比你能夠建一個「D:\Masm」目錄,並建議此後的程序都放這個目錄,此後稱這個目錄爲彙編目錄。

二、學習彙編須要有哪些編程方面的知識?

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

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

三、學彙編有什麼用?

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


第零講 預備知識


一、一個彙編程序的編譯過程是怎麼樣的?windows

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

二、宏彙編和彙編有什麼區別嗎?

兩者的區別在於前者提供宏,後者不提供。後者已找不到了,因此你能夠認爲兩者沒有區別。

三、機器語言、彙編語言、高級語言的關係併發

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

四、計算機的組成框架

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

五、寄存器和內存的區別編輯器

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

六、彙編語言的計數函數

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

七、進制問題

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

八、進制轉換

一個比較簡單的方法是查表法。
十進制 十六進制 二進制
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


第一講 基礎知識

一、訪問內存

程序在內存中,訪問內存是幾乎每一程序都要進行的操做,計算機對內存編址是線性的,也就是說是一維的,好比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。這段內存的容易至關於一個普通文檔的大小。不過這就足夠了。

二、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
--------------------------小抄結束--------------------------------

三、驗證第一節裏的內容

運行「開始/程序/附件/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也確定是指這一個地址,不信試試。

四、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「-」提示符。若是在修改過程當中,將空格鍵換成按「-」鍵,則表示能夠修改前一個單元的內容。
----------------------小抄結束------------------------------

五、使用DOS時,彙編用戶能夠從DOS操做系統中獲得什麼?

如今編程,一般不少功能都是經過調用系統API。不少高級語言都直接把這些API包裝起來,以系統接口或函數的方式提供給用戶,那麼彙編函數都能獲得什麼呢?
首先,彙編用戶有不少東西能夠調用。他們主要是:

5.1 BIOS提供的接口。如今硬件與軟件的區分已愈來愈不明顯,不少硬件不只僅是電路,而還要提供一些固化寫入硬件的一部分「程序」,這些程序以ROM的方式出現,彙編用戶最大的好處就是能夠直接使用這些「程序」,這些使用不只功能強大,並且效率很是高。

5.2 DOS功能調用,做爲操做系統也象BIOS同樣向用戶提供了相應的「程序」。這些程序在很大程序上擴充了BIOS。與BIOS不一樣的是,這部分程序放在內存中,它能夠被修改。而BIOS中不能再修改。
==========================================================
以上兩種接口都經過一種相同的格式調用,這些程序統稱爲「中斷」,如今先不要理解中斷的本意,你如今能夠認爲是系統提供給你的函數。
============================================================

5.3 系統共享數據區。編過程序的人都知道全局變量的好處,全局變量方便以外在於任何函數、過程均可以調用、讀取、修改。全局變量不足之處是危險性,有一個過程改了這個變量值,其它的也得跟着改變了。DOS操做系統一樣也提供了這樣的共享數據區,該區是整個系統的共享區,任何程序均可以查找、修改。固然,修改某處必然會對其它程序形成影響。

六、再談中斷

前面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。

七、再談系統共享數據區

該共享數據區在絕對地址:0040:0000開始。

八、驗證我上面說的內容

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)就丟了。兄弟們誰找到這個地址的內容,必定要貼上來,這裏有東西可讓你們眼界大開。
前幾年,我用的286計算機是黑白顯示器(555555~~~~~~~~~,別嫌我老、舊、慢呀),可當時有個遊戲非要彩顯,不是彩顯不讓運行。我就是改了這個區的某一個位,讓哪遊戲「覺得」我用的是彩顯,因而遊戲能用了。雖然很差看,但總能用。
在DOS下,你每按一個鍵,系統都會記下來,下面咱們一塊兒找找這個鍵盤緩衝區的地址。知道這個地址,你就能夠做一個「虛擬」鍵盤,經過發命令來模擬某我的在按鍵。這個地址位於:0040:001E。 其中每一個鍵有兩個字節,一個字節是ASCII碼,一個是掃描碼。共16個。

C:\>debug
-d 40:0
0040:0000 F8 03 F8 02 E8 03 E8 02-BC 03 78 03 78 02 80 9F ..........x.x...
0040:0010 22 C8 00 80 02 28 00 00-00 00 2A 00 2A 00 20 39 "....(....*.*. 9
0040:0020 34 05 30 0B 3A 27 30 0B-0D 1C 64 20 20 39 34 05 4.0.:'0...d 94.
0040:0030 30 0B 3A 27 30 0B 0D 1C-71 10 0D 1C 64 20 00 00 0.:'0...q...d ..
0040:0040 A2 00 C3 00 A2 AF 09 E1-C8 03 50 00 00 10 00 00 ..........P.....
0040:0050 00 18 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0040:0060 0F 0C 00 D4 03 29 30 7F-03 00 C0 00 A1 B7 11 00 .....)0.........
0040:0070 00 00 00 00 00 00 00 00-14 14 14 00 01 01 01 01 ................
-d 0040:0000
0040:0000 F8 03 F8 02 E8 03 E8 02-BC 03 78 03 78 02 80 9F ..........x.x...
0040:0010 22 C8 00 80 02 28 00 00-00 00 2A 00 2A 00 3A 27 "....(....*.*.:'
0040:0020 30 0B 30 0B 30 0B 30 0B-0D 1C 64 20 20 39 30 0B 0.0.0.0...d 90.
0040:0030 30 0B 30 0B 30 0B 08 0E-08 0E 34 05 30 0B 00 00 0.0.0.....4.0...
0040:0040 1F 00 C3 00 A2 AF 09 E1-C8 03 50 00 00 10 00 00 ..........P.....
0040:0050 00 18 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0040:0060 0F 0C 00 D4 03 29 30 7F-03 00 C0 00 24 B8 11 00 .....)0.....$...
0040:0070 00 00 00 00 00 00 00 00-14 14 14 00 01 01 01 01 ................
-
既然是鍵盤緩衝區,每一個輸入的鍵都會顯示在該區中,第一次我只輸入了「d 40:0」,因此你能夠在此後顯示數據右邊字符中找到這些字符,注意是間隔開的。
第二次我輸入「d 0040:0000」,則右邊顯示的是「d 0040:0000」的內容。你能夠找找。


第二講 內存映象

之因此把這個內存單獨放一章,是爲了說明它的重要性,後面的幾乎不少程序都須要你對這一章的理解。這裏的內存映象就是指當你把一個可執行文件(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文件。從程序內部看,前者程序的四個段重合(後面要講這四個段),因此最大長度只等於一個段,用前面段地址的理解就是com文件最大隻能是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的API。「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×25。每一個位置有兩個值,一個值是ASCII字符,一個值是該ASCII的屬性值(主要是顏色)。因此一個屏幕共有80×25×2=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
變了嗎?

2.3 可執行文件內存映象

DOS下可執行文件有兩種(BAT是批處理文件,他只是簡單調用DOS內部命令或其它程序,因此此處不認爲它是可執行文件):一種是COM文件,一種是EXE文件,前面提到,COM文件通常小於64K。EXE文件則能夠任意大。爲何呢?
說到這裏,還要提到段。每一個段64K。段的做用就是數據組織單位。段的類型有三種:代碼段(Code Segment,簡稱CS)、數據段(Data Segment,簡稱DS)、棧段(Stack Segment,簡稱SS),另外還有一個附加數據段(Extra Segment,簡稱ES),它的用與數據段DS能夠認爲徹底同樣,當數據段的64K不夠用,或你就須要把數據放到兩個段中以便移動、複製、比較時,纔用到附加數據段ES。(固然,移動、複製、比較操做在一個段中也能夠完成)。

1.段的做用

1.1 代碼段(CS):程序裝入內存中,DOS怎麼知道是從哪裏執行呢?答案就是系統自動從代碼段指定位置開始執行,而且始終在代碼段中執行。所以代碼段CS的做用就是保存全部的指令。這裏所說的代碼也就是彙編指令了。因此編寫彙編程序也就主要是編寫代碼段中的代碼。

1.2 數據段(DS)、附加段(ES):顧名思義,數據段中存的就是數據,這些數據供代碼段的程序調用。附加段就是附加數據段。做用與數據段相同。

1.3 棧段(SS):這個段很是重要,但實際上,你在使用中,彷佛用不着這個段,但實際上,這是黑客編程中最重要的一部分,並且系統會不停地「偷偷地」使用這個段,正是這個偷偷地用,使得系統的不少動做被記錄到這個段中。還有兩點,你必須記住:一是若是你使用了這個棧,好比你把數據存到這個棧中,則必須有相應的出棧命令,而且入幾個數據,就得出幾個數據,多一個或少一個,你的程序就可能致使死機或異常;二是你要把握操做時機,好比你不能在系統使用棧的先後使用棧,好比你在調用子程序以前入棧,而在子程序中出棧,而在系統調用子程序時,系統也要使用棧,這種也將致使出錯。
棧就是一種先入後出(也有稱爲後入先出)的結構,有地址由小到大的增長棧,有地址由大到小的逆向減棧。

2.段重疊

從上面,咱們能夠看到,CS,DS,SS三者做用各不相同,內存就是象錄音磁帶,錄新歌,則舊歌被刪,帶子上存的始終是最後錄的那段音樂。所以,若是重疊則必然相互衝突。那還能重疊嗎?
這裏所說的重疊不是指內容重疊,而是指概念上的重疊,即數據相互放到一個段中,但相互能夠區分開。好比某一段既有數據也有代碼,則代碼在每要執行到數據以前加一個跳轉指令跳過這段代碼。這個跳轉指令要求用戶在編程的時候加上。
而棧段呢?棧段有本身的特殊性,特殊就在於系統也會自動地使用,而用戶則又在不知道系統在使用的狀況下使用。避免這種衝突的方法就是採用逆向的棧段。

3 .COM文件內存映象

COM文件被讀到內存中後,該文件的前100H個字節被操做系統使用,操做系統使用這256個字節保存一些系統要使用的數據,彙編語言編程者不能在這裏存本身的數據,但在知道這此數據的做用後可使用其中的數據。從100H開始,就是程序的開始了。COM文件之因此最大隻能有64K,其緣由是COM文件的四個段是相互重疊的。也就是說,CS、DS、SS、ES四個段的地址都指向這個COM文件的100H處。程序代碼、數據、棧都在由100H到64K的區域內。如何把三者分開呢?棧段採用逆向棧,這個棧由64K開始,隨着數據入棧,則地址就減少。這樣做的好處是,棧段由高端向低端進展,能夠詳細與數據、代碼分開;壞處也不言而喻,假如一個COM程序大量用到棧(好比是個遞歸程序)所以棧就不停地下降,而程序代碼自己也不少,甚至不停地申請新空間,這樣數據和棧就會在中間碰頭,致使程序被破壞。
區分開數據代碼段與棧段後,下面討論把數據段和代碼段也分開。這個簡單的多,只要邏輯上分開就能夠。不過通常的方法就是:在100H處放一個跳轉指令,隨後放數據,而後再放置其它的代碼。而100H處的跳轉指令就跳到這裏。
所以,COM文件內存映象就是:
CS:0000 (因爲COM的CS,DS,SS,ES三段重疊,所以此行前CS,寫成DS,SS,ES都同樣)
CS:0100 一個跳轉到YYYY地址的跳轉指令。
CS:0101 本程序所須要用到的數據
CS:XXXX 數據結束處。
CS:YYYY 程序代碼保存處。
CS:ZZZZ 程序代碼結束處。
CS:FFFF 棧段開始處(注意棧是地址愈來愈小,因此這裏是開始而不是結束處),也是程序的結束處。另外,此處FFFF與前面XXXX,YYYY,ZZZZ不同,這裏是十六進制的64K。

4.EXE文件

比起COM文件,EXE文件要複雜一些,他的複雜就在於COM文件前面規定了100H個字節用於系統使用,而EXE文件則有個文件頭,文件頭的大小看具體內容多少。文件頭的內容使得EXE看起來複雜了,但也更靈活了。更重要的是,對於病毒設計者,這個文件頭使他們如魚得水。由於文件頭處
EXE文件的內存映象爲:
XXXX:0000 文件頭
XXXX:YYYY 文件頭結束處

CS:0000 代碼段開始處
CS:ZZZZ 代碼段結束處

DS:0000 數據碼段開始處
DS:WWWW 數據碼段結束處

SS:0000 棧段開始處
SS:UUUU 棧段結束處

ES:0000 附加段開始處
ES:VVVV 附加段結束處

說明:
1) 上述ES能夠沒有,要看實際須要
2) CS,DS,ES,SS的順序也是看編程者是怎麼安排的,好在用戶沒必要關心他的具體位置。
3) 由上可見,CS,DS,ES,SS的段地址確定保存到了文件頭中。
4) 由上可見,實際執行的只是CS,所以DS,ES,SS的首地址,CS確定要想辦法知道。:)


第三講 彙編指令

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<OP2 )
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

相關文章
相關標籤/搜索