天天看點

看完這篇文章就不要再問我彙編了

先來個小栗子再來解釋:

看完下面的内容相信你已經知道彙編怎麼注釋了。

;.asm檔案命名不能超過8個英文字元,否則不能打開
;我的學号為52013149520,學号的最後一位為0,編寫程式測試6B7AH的第0位,如果為0,螢幕上顯示0,否則螢幕顯示1
;我用的是根據學号數将DX=0001H左移幾位,測試值和DX相與,再和DX比較,根據标志跳轉,進而輸出0或1;
;也可以采用TEST等等
data SEGMENT  					;資料段,程式設計者可以把資料都放到這個段裡
	num dw 0,6B7AH	 			;學号最後一位(0~9),測試數值    啥都不加的時候,預設是10進制的
data ENDS      					;資料段結束處。
								;6B7AH=‭0110101101111010‬B	位數從右往左數	
zhengqian SEGMENT              	;代碼段,實際的程式都是放這個段裡,此處zhengqian為代碼段名。
	ASSUME CS:zhengqian,DS:data ;告訴編譯程式,data段是資料段DS,code段是代碼段CS			
    start:	MOV AX,data			;前面的start表示一個辨別位(可省略),後面用到該位,如果用不到,可不加
			MOV DS,AX         	;這一句與上一行共同組成把data指派給DS段寄存器.
			MOV CL,DS:[0]   
			MOV BX,DS:[2]       ;此時bx内為6B7Ah
			MOV DX,0001H
			SHL DX,CL      		;邏輯左移CL位	
			AND BX,DX			;與操作,得bx,我的此時為0
			CMP BX,DX      		;再和dx比較,相等時結果為0,ZF=1 零标志,跳轉,輸出1 ,我的現在不相等不需跳轉
			JZ	eql				
			MOV DL,48			;輸出0
			JMP s				;無條件跳轉
	  eql:  MOV DL,49           ;輸出1
	    s:	MOV AH,02H			;2号功能
			INT 21H 
			MOV AX,4C00H      	;程式退出,該句記憶體由下一行決定。退出時,要求ah必須是4c。
			INT 21H             ;記住規定這樣
zhengqian ENDS          		;代碼段結束。
END start           			;整個程式結束,并且程式執行時由start那個位置開始執行。

;ptr-pointer(指針)縮寫保留字  mov ax,word ptr [bx]<===>MOVE AX,BX因為同類型是以不用word  是把記憶體位址等于“BX寄存器的值”的地方所存放的資料,賦予ax    
 ;cmp WORD PTR[SI+BX],0	;拿一個“字”,和零,相比較	


           

其實我完全可以用test更快更好的解決,我為什麼要這樣寫呢。

(因為一開始學更好了解一些)

進入正題:

先學幾個指令8

資料傳送指令:

把資料從一一個位置傳送到另一個位置

計算機中最基本的操作

程式設計中最常使用的指令

除标志寄存器傳送指令外,均不影響狀态标志

MOV XCHG PUSH POP LEA

MOV (Move): 最常用的資料傳送指令

功能: 把源操作數送給目的操作數

文法: MOV 目的操作數,源操作數

格式:

MOV AX,2000H;将16位資料2000H傳送到AX寄存器

MOV AL,20H;将8位資料20H傳送到AL寄存器

MOV AX,BX;将BX寄存器的16位資料傳送到AX寄存器

MOV AL,[2000H];将2000H單元的内容傳送到AL寄存器

将一個資料從源位址傳送到目标位址(寄存器間的資料傳送本質上也是一樣的)

1.兩個存儲單元之間不能直接傳送資料

即:MOV指令隻允許一個操作數在存儲器中。MOV [SI],[2000H];這是錯誤的

2.MOV指令中立即數不能直接傳送給段寄存器(CS、DS、SS、ES)和IP;段寄存器之間不能直接傳送。MOV IP,2000 H ;這是錯誤的

3. CS和IP不能作為目的操作數。MOV CS,AX ;這是錯誤的

4. MOV指令中立即數不能作目标操作數。MOV 2000H,[SI] ;這是錯誤的

MOV指令可以在CPU内或CPU和存儲器之間傳送字或位元組,它傳送的資訊可以從寄存器到寄存器,立即數到寄存器,立即數到存儲單元,從存儲單元到寄存器,從寄存器到存儲單元,從寄存器或存儲單元到除CS外的段寄存器(注意立即數不能直接送段寄存器),從段寄存器到寄存器或存儲單元。

MOV指令不會影響标志位

MOV指令中絕對不允許在兩個段寄存器之間直接傳送資料

MOV指令中絕對不允許在兩個存儲單元之間直接傳送資料

段寄存器(段位址)必須通過寄存器如AX寄存器進行立即數的初始化

MOV AL,‘E’

把立即數(字元E的ASC碼)送到AL寄存器

MOV BX,OFFSET TABLE

将TABLE的偏移位址(而不是内容)送到BX寄存器中。其中OFFSET為屬性操作符,表示的是将其後的符号位址的值(不是内容)作為操作數

MOV AX,Y[BP][SI]

把位址為16d×(SS)十(BP)十(SI)十位移量Y的存儲單元的内容送給AX寄存器

XCHG (Exchange): 交換指令

功能: 交換兩個操作數的資料

文法: XCHG

格式: XCHG r1,r2 XCHG m,r XCHG r,m

交換指令XCHG是兩個寄存器,寄存器和記憶體變量之間内容的交換指令,兩個操作數的資料類型要相同,可以是一個位元組,也可以是一個字,也可以是雙字。寄存器不能是段寄存器,兩個操作數也不能同時為記憶體變量。XCHG指令不影響标志位。

1. 不能同時都為記憶體操作數

2. 任何一個操作數都不能為段寄存器

3. 任何一個操作數不能為立即數

4. 兩個操作數的長度不能不相等

XCHG指令的功能和MOV指令不同,MOV指令是一個操作數的内容被修改,而前者是兩個操作數都會改變。

PUSH,POP

功能: 把操作數壓入或取出堆棧

文法: PUSH操作數POP 操作數

格式: PUSH r PUSH M PUSH data POP r POP m

PUSH F, POP F, PUSH A, POP A

功能:堆棧指令群

push寄存器:将一個寄存器中的資料入棧

pop寄存器:出棧用一個寄存器接收資料

push pop 隻對字操作(不允許位元組進棧)操作數長度為32位時進出棧為雙字

如 push dl是不正确的,應該是push dx

PUSH導緻SP減2而不是加2。原因:棧在記憶體中實際存儲結構是棧底在高位址,棧頂在低位址(x86中的棧是“滿遞減棧”,也就是sp指向的棧的記憶體單元中是滿的,有内容的,而且push資料時,棧頂對應的位址是遞減的)

8086push不可以使用立即數尋址方式(其他版本允許)

pop不可以使用立即數尋址方式,使用段寄存器時不可使用CS段寄存器

SP是個寄存器,SP的内容是位址,sp指向棧頂,而棧頂的内容是什麼與(sp)是什麼無關。

LEA,LDS,LES

功能: 取位址至寄存器

文法: LEA r,m LDS r,m LES r,m

LEA指令傳回間接操作數的偏移位址,間接操作數可能使用一個或多個寄存器,

是以其偏移值是在運作時計算的。 位址傳送指令load effective address

位址傳送指令擷取存儲器操作數的位址

LEA指令類似位址操作符OFFSET的作用

跟offset的差別:

lea edi, var mov edi, offset var

看完這篇文章就不要再問我彙編了

LEA(Load Effective Address): 有效位址傳送指令

LEA指令在指令執行時計算出偏移位址

OFFSET操作符在彙編階段取得變量的偏移位址

0FFSET無需在執行時計算、指令執行速度更快

LEA指令能擷取彙編階段無法确定的偏移位址

LEA指令是在執行過程中獲得位址,當無法在彙編階段獲得位址時,就用LEA指令

看完這篇文章就不要再問我彙編了
看完這篇文章就不要再問我彙編了

LEA BX,[SI] ;BX<–DS:[SI]記憶體單元的偏移位址,與該單元中的資料無關

LDS BX,[SI] ;BX<–DS:[si]記憶體單元中的資料的低16位,DS<–記憶體單元中資料的高16位

LES BX,[SI] ;BX<–DS:[SI] ,ES<–DS:[SI+2]

顯然LDS和LES的操作數是32位的(兩個字 四個位元組 32位)

LEA隻是傳送DS:[]指向記憶體單元的偏移位址

LDS的傳送并不是偏移位址而是實實在在的記憶體單元中的資料

LDS REG,OPRE;REG是一個16位寄存器,OPRD是一個存儲器操作數,為雙字項,高16位送入DS,低16位送入REG

LDS BX,[SI] (ds:si取得存儲器中32位(雙字),高16位作為段值送入DS中,低十六位送入指定的BX寄存器中)

XLAT(XLATB) XLATB(Translate Byte)、XLAT(Translate): 換碼指令

功能:查表指令 XLATB 可簡化為 XLAT

文法: XLAT XLAT m XLAT 無參數, 操作和 EBX、AL 相關

查表,就是查找表中元素

DATA SEGMENT

A DB ‘HELLO WORLD’,

'$';

’$'作為停止

DATA ENDS

XLAT指令可用于數字的加密

算數運算指令

ADD,ADC ( ADD with Carry)帶進位加法指令

功能: 加法指令 //Byte/Word

文法: ADD OP1,OP2 ADC OP1,OP2 //與ADD不同是要加上進位标志位的值.

格式: ADD r1,r2 ADD r,m ADD m,r ADD r,data

影響标志: C,P,A,Z,S,O

兩個存儲器操作數不能通過ADD指令直接相加,必須有一個是通用寄存器操作數.

段寄存器不能作為SRC 和DST

影響标志位Auxiliary Crray Flag ,Carry Flag, Overflow Flag, Parity Flag, Sign Flag 和Zero Flag

看完這篇文章就不要再問我彙編了

SUB ( SUBtract )減法指令,SBB( SuBtract with Borrow) 帶借位減法指令

功能:減法指令

文法: SUB OP1,OP2 SBB OP1,OP2

格式: SUB r1,r2 SUB r,m SUB m,r SUB r,data SUB m,data

影響标志: C,P,A,Z,S,O

INC ( INCreament) 加1指令,DEC

功能: 把OP的值加一或減一 //Byte/Word

文法: INC OP DEC OP OP是寄存器和存儲器操作數, 不能是立即數和段寄存器

格式: INC r/m DEC r/m

影響标志: P,A,Z,S,O,不影響CF

MUL( unsigned MULtiple )乘法指令,IMUL (sIgned MULtiple) 有符号數乘法指令

功能: 乘法指令 目的數必須是累加器 AX 或AL,指令中不需寫出

文法: MUL OP IMUL OP //與MUL指令相同,但必須是帶符号數

格式: MUL r/m IMUL r/m

影響标志: C,P,A,Z,S,O(僅IMUL會影響S标志)

//位元組運算時目的操作數用AL, 乘積放在AX中

//字運算時目的操作數用AX, DX存放乘積的高位字, AX放乘積的低位字

源操作數SRC可以是通用寄存器和各種尋址方式的存儲器操作數, 而絕對不允許是立即數或段寄存器.

DⅣ( unsigned DIVide)除法指令,IDⅣ(sIgned DIVied) 有符号數除法指令

功能:除法指令 //位元組運算時目的操作數在AX中,結果的商在AL中 ,餘數在AH中

文法: DⅣ OP IDⅣ OP

//字運算時目的操作數在DX高位字和AX低位字中,結果的商在AX中 ,餘數在DX中

格式: DⅣ r/m IDⅣ r/m

邏輯運算指令

NEG( NEGate ) 求補指令

功能: 将OP的符号反相(取二進制補碼)

文法: NEG OP //将操作數按位求反後末位加1

格式: NEG r/m

影響标志: C,P,A,Z,S,O

CBW(Convert Byte to Word) 位元組轉換為字指令,

執行操作: AL中的符号位(D7)擴充到8位AH中,若AL中的D7=0,則AH=00H,若AL中的D7=1,則AH=FFH.

CWD(Convert Word to Double word) 字轉換為雙字指令

執行操作: AX中的符号位(D15)擴充到16位DX中,若AX中的D15=0,則DX=0000H,若AX中的D15=1,則DX=FFFFH.

功能:有符号數擴充指令

文法: CBW CWD

可用4位二進制數表示一位十進制數,這種代碼稱為BCD ( Binary Coded Decimal ).

BCD碼又稱8421碼,在PC機中,BCD碼可用壓縮的BCD碼和非壓縮的BCD碼兩種格式表示.

AAA (ASCII Adjust for Addition) 加法的ASCII調整指令,

執行操作:執行之前必須先執行ADD或ADC指令,加法指令必須把兩個非壓縮的BCD碼相加,并把結果存話在AL寄存器中.

AAS (ASCII Adjust for Subtraction) 減法的ASCII調整指令,

執行操作:執行之前必須先執行SUB或SBB指令,減法指令必須把兩個非壓縮的BCD碼相減,并氫結果存放在AL寄存器中.

AAM,AAD

功能: 非壓BCD碼運算調整指令

文法: AAA AAS AAM AAD

影響标志: A,C(AAA,AAS) S,Z,P(AAM,AAD)

DAA (Decimal Adjust for Addition) 加法的十進制調整指令,

執行操作:執行之前必須先執行ADD或ADC指令,加法指令必須把兩個壓縮的BCD碼相加,并把結果存話在AL寄存器中

DAS (Decimal Adjust for Subtraction) 減法的十進制調整指令

執行操作:執行之前必須先執行SUB或SBB指令,減法指令必須把兩個壓縮的BCD碼相減,并氫結果存放在AL寄存器中.

功能:壓縮BCD碼調整指令

文法: DAA DAS

影響标志: C,P,A,Z,S

位運算集

AND,OR,XOR,NOT,TEST

功能: 執行BIT與BIT之間的邏輯運算

文法: AND r/m,r/m/data OR r/m,r/m/data XOR r/m,r/m/data TEST r/m,r/m/data NOT r/m

影響标志: C,O,P,Z,S(其中C與O兩個标志會被設為0) NOT指令不影響任何标志位

SHR,SHL,SAR,SAL

功能:移位指令

文法: SHR r/m,data/CL SHL r/m,data/CL SAR r/m,data/CL SAL r/m,data/CL

影響标志: C,P,Z,S,O

ROR,ROL,RCR,RCL

功能: 循環移位指令

文法: ROR r/m,data/CL ROL r/m,data/CL RCR r/m,data/CL RCL r/m,data/CL

影響标志: C,P,Z,S,O

程式流程控制集

CLC,STC,CMC

功能: 設定進位标志

文法: CLC STC CMC

标志位: C

CLD,STD

功能: 設定方向标志

文法: CLD STD

标志位: D

CLI,STI

功能: 設定中斷标志

文法: CLI STI

标志位: I

CMP( CoMPare ) 比較指令

功能: 比較OP1與OP2的值

執行操作:OPR1 - OPR2 //與SUB指令一樣執行運算, 但不儲存結果

文法: CMP r/m,r/m/data

标志位: C,P,A,Z,O

看完這篇文章就不要再問我彙編了

JMP

功能: 跳往指定位址執行

文法: JMP 位址

JXX

功能: 當特定條件成立則跳往指定位址執行

文法: JXX 位址

注:

A: ABOVE,當C=0,Z=0時成立

B: BELOW,當C=1時成立

C: CARRY,當弁時成立 CXZ: CX寄存器的值為0(ZERO)時成立

E: EQUAL,當Z=1時成立

G: GREATER(大于),當Z=0且S=0時成立

L: LESS(小于),當S不為零時成立

N: NOT(相反條件),需和其它符号配合使用

O: OVERFLOW,O=1時成立

P: PARITY,P=1時成立

PE: PARITY EVEN,P=1時成立

PO: PARITY ODD,P=0時成立

S: SIGN,S=1時成立

Z: ZERO,Z=1時成立

LOOP

功能: 循環指令集

文法: LOOP 位址

LOOPE(Z)

位址 LOOPNE(Z) 位址

标志位: 無

CALL,RET

功能:子程式調用,傳回指令

文法: CALL 位址 RET RET n

标志位: 無

INT,IRET

功能: 中斷調用及傳回指令

文法: INT n IRET

标志位: 在執行INT時,CPU會自動将标志寄存器的值入棧,在執行IRET時則會将堆棧中的标志值彈回寄存器

串操作集

MOVSB,MOVSW,MOVSD

功能: 字元串傳送指令

文法: MOVSB MOVSW MOVSD

标志位: 無

CMPSB,CMPSW,CMPSD

功能: 字元串比較指令

文法: CMPSB CMPSW CMPSD

标志位: C,P,Z,S,O

SCASB,SCASW

功能: 字元串搜尋指令

文法:SCASBSCASW

标志位: C,P,Z,S,O

LODSB,LODSW,STOSB,STOSW

功能: 字元串載入或存貯指令

文法:LODSBLODSWSTOSBSTOSW

标志位: 無

REP,REPE,REPNE

功能: 重複字首指令集

文法: REP 指令S REPE 指令S REPNE 指令S

标志位: 依指令S而定

既然知道常用指令了,加上工具我們就可以來試試啦!

masm.exe和link.exe必不可少!

來吧!一大波資源

windows下快捷鍵win+R輸入cmd進入你寫的檔案名.asm目錄下:

(忘了win10要dosbox))

彙編常用指令及實際操作:

先記住以下兩個指令:D指令和Q指令。前者是顯示記憶體内容,後者是退出DEBUG指令。

退出指令 Q

格式:Q

功能:退出DEBUG,傳回到作業系統。

顯示存儲單元指令 D

格式1:D[起始位址]

格式2:D[起始位址][結束位址|位元組數]

功能:格式1從起始位址開始按十六進制顯示80H個單元的内容,每行16個單元,共8行,每行右邊顯示16個單元的ASCII碼,不可顯示的ASCII碼則顯示“·”。格式2顯示指定範圍記憶體儲單元的内容,其他顯示方式與格式1一樣。如果預設起始位址或位址範圍,則從目前的位址開始按格式1顯示。

例如: -D 200 ;表示從DS:0200H開始顯示128個單元内容

-D 100 120742 ;表示顯示DS:0100-DS:0120單元的内容

073F:011C=0750C === 0700:050C

實體位址=段基值(二進制左移4位)+偏移位址(相對位址)

可先-d 顯示出存儲單元

可看到073F:011C處為34,根據邏輯位址和實體位址關系,可得實體位址唯一。

輸入-d 0700:050C可得出相應存儲單元的值也是34,以此類推同樣如此,相當于我們學習可以通過老師教授,或者網上聽課,或者通過看書等等。

看完這篇文章就不要再問我彙編了

修改存儲單元指令 E      關閉後重新打開會恢複預設值

格式1:E[起始位址] [内容表]

格式2:E[位址]

功能:

格式1按内容表的内容修改從起始位址開始的多個存儲單元内容,即用内容表指定的内容來代替存儲單元目前内容。

格式2是逐個修改指定位址單元的目前内容。

看完這篇文章就不要再問我彙編了

輸入—E DS:0100 ‘var’ 12 34

從DS:0100 為起始單元的連續五個位元組單元内容依次被修改為’V’、‘A’、‘R’、12H、34H

輸入—E DS:0010之後,出現下面語句(除了5F是我輸入的修改值):

073F:0010 A3.5F

其中073F:0010單元原來的值是A3H,5FH為輸入的修改值。

若隻修改一個單元的内容,這時按Enter鍵即可;

若還想繼續修改下一個單元内容,此時應按空格鍵,就顯示下一個單元的内容,需修改就鍵入新的内容,不修改再按空格跳過,如此重複直到修改完畢,按Enter鍵傳回“-”提示符。

如果在修改過程中,将空格鍵換成按“-”鍵,則表示可以修改前一個單元的内容。

看完這篇文章就不要再問我彙編了

彙編使用者有很多東西可以調用。他們主要是:

1、BIOS提供的接口。硬體與軟體的區分已越來越不明顯,很多硬體不僅僅是電路,而還要提供一些固化寫入硬體的一部分“程式”,這些程式以ROM的方式出現,彙編使用者最大的好處就是可以直接使用這些“程式”,這些使用不僅功能強大,而且效率非常高。

2、DOS功能調用,作為作業系統也象BIOS一樣向使用者提供了相應的“程式”。 這些程式在很大程式上擴充了BIOS。與BIOS不同的是,這部分程式放在記憶體中,它可以被修改。而BIOS中不能再修改。

以上兩種接口都通過一種相同的格式調用,這些程式統稱為“中斷”,現在可以将其認為是系統提供給你的函數。

3、系統共享資料區。編過程式的人都知道全局變量的好處,全局變量友善之外在于任何函數、過程都可以調用、讀取、修改。全局變量不足之處是危險性,有一個過程改了這個變量值,其它的也得跟着改變了。DOS作業系統同樣也提供了這樣的共享資料區,該區是整個系統的共享區,任何程式都可以查找、修改。當然,修改某處必然會對其它程式造成影響。

共享資料區在絕對位址:0040:0000開始。

中斷:現在問題是不同硬體不一樣,即使相同硬體的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

找中斷:

看完這篇文章就不要再問我彙編了
看完這篇文章就不要再問我彙編了

首先,D指令把中斷首位址顯示出來。每4個表示一個位址。其中INT 0的中斷首位址為:F000:1060,INT 1的中斷位址為:0070:0008……0070:0008是中斷3的首位址。

後面那個U指令就表示顯示該位址的“中斷程式”的記憶體。

可以試着找找INT 15的位置?F000:1060

驗證系統共享資料區

系統共享資料區内容極為豐富,實在記不住哪麼多了,

在DOS下,你每按一個鍵,系統都會記下來,下面我們一起找找這個鍵盤緩沖區的位址。

知道這個位址,你就可以作一個**“虛拟”鍵盤**,通過發指令來模拟某個人在按鍵。

這個位址位于:0040:001E。 其中每個鍵有兩個位元組,一個位元組是ASCII碼,一個是掃描碼。共16個。

看完這篇文章就不要再問我彙編了

既然是鍵盤緩沖區,每個輸入的鍵都會顯示在該區中。

第一次我輸入了“d 40:0”,你可以在此後顯示資料右邊字元中找到這些字元,是間隔開的。

第二次我輸入“d 0040:0000”,則右邊顯示的是“d 0040:0000”的内容。你也可以找找。

第二講 記憶體映象

之是以把這個記憶體單獨放一章,是為了說明它的重要性

記憶體映象就是指當你把一個可執行檔案(EXE或COM檔案)放到記憶體後,整個記憶體“看”起來是什麼樣子的。

彙程式設計式隻能通路1M的記憶體空間,是以下面就以1M記憶體為例。

以DOS作業系統作為講解對象,是以所編出來的程式也僅是DOS程式。

通過winasm可以通路遠遠超過1M的空間,并且可以編出FOR windows的程式。

這1M記憶體如果我們不再以二維的方式看,而是一維的,線性地看。但描述還是以二維的方式描述,從最底端到最高端依次是:

1 中斷向量區:該區由0000:0000~0000:03FF。這裡存着系統的所有中斷的中斷向量表,對于中斷向量表,你現在先了解為一些程式的首位址。由這個位址你就能找到該程式。

2 系統資料區:該區由0040:0000~0040:XXXX(不好意思,忘了),這裡存着整個系統中,DOS作業系統要用的資料,由于這個區的資料對使用者是開放的,是以使用者當然也可以從這裡讀出來用。

3 DOS作業系統區:作業系統常駐記憶體,你向計算機發的每個指令其實都是作業系統執行的。這個區的大小主要是由作業系統的版本和使用者的配置大小決定,如果是驅動程式配置,就放到根目錄下的config.sys裡,如果是程式,就放到autoexec.bat裡。

4 使用者程式,這個當然就是你執行的程式了,這種程式分兩種,一種是擴充名為com檔案,一種是exe檔案,com檔案最大隻能是64K,是以com檔案隻适合小的程式。而exe,四個段可任何配置設定,并可擴充段,而且每個段的段位址可以任何改動,是以exe的通路記憶體能力大多了。這種格式通路能力隻受位址結構的限制了。

使用者程式所占的記憶體大小完全由程式本身決定,但最大,隻能到640K。隻能怪目前計算機軟硬體設定高手高手高高手們(包括比爾蓋茨)們的失誤了,60年代的超級計算機隻有36K的記憶體,是以他們就在80年代得到一個結論:640K的記憶體足夠了。

如果使用者程式大于由作業系統所占記憶體的頂底到640K之間的記憶體量,就會顯示:記憶體不夠,因而程式不能執行。如果小于這段記憶體,多餘部分就空着。

5 從640K到1M-64K,這段記憶體就很難說清了。這段記憶體中有一部分被硬體占有,有一部分是顯示緩沖區點有,還有一部分是系統ROM占有。

6 從1M-64K到1M之間的這段64K的記憶體叫作HMA高端記憶體區(high memory area)。這段記憶體是小孩沒娘,說來話長,我們先不說他。

中斷向量表就是所有中斷向量首位址表,這裡儲存着每個中斷程式的首位址,幾乎所有的彙編書都把中斷後面後面的章節中,并且對中斷的解釋也僅從字面意思解釋,是以導緻大學對中斷的不重要和誤解。沒耐心的沒到這個章節就不學彙編了,有耐心的到這裡才豁然開朗。我現在不講中斷的原意。我直接告訴你,你把中斷當成API也許更合适。也就是說,别人把很多已作好的功能放到了記憶體中。并且把調用這一功能的号告訴了你,你隻要調用這些功能号,系統就自動從這個中斷向量表中找到對應的中斷,然後執行你的功能。

讓你感受一下中斷的魅力一下吧。比如中斷21H的2A功能調用是讀取系統的日期,這個調用的規則是,調用前AH寄存器置為2A。調用後年在CX中,月在DH中,日在DL中,星期在AL中。

看完這篇文章就不要再問我彙編了

mov ah,2a”表示調用功能号是2a号。“int 21”表示調用十六進制21号中斷,“int 3”表示3号中斷,表示程式運作到這一句時停一下。“g=100”表示從“073F:0100 ”開始執行。

CX=07E4H=2020 DH=03H=3 DL=1EH=30 AL=01H=1 (今天是2020.3.30周一)

AX=AH+AL DH+DL=DX 僅僅兩行指令,就讀到了現在的值

原來你在彙編裡運作int 21時,系統就在上面的中斷向量表中找到int 21的中斷位址,該中斷的位址應該位于:0000:0084~0000:0087。

找到内容是:A014:00F0。然後系統就轉到這個位址執行int 21。

系統區,很多DOS中斷程式實作部分就在這個區。

640K~1M之間,這期間有些地方是ROM,有些地方是硬體的BIOS區。

ROM區:ROM區就是隻讀記憶體,也就是說這個區的資料隻能讀不能寫。

看完這篇文章就不要再問我彙編了

通過上面測試,(其中66 55 44 33 22 11是我想改的值)發現該區資料仍然未改變。但你要是試别的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

變了嗎?

第三章 彙編指令

什麼是機器語言?

前面提到“最早的計算機采用機器語言,這種語言直接用二進制數表示,通過直接輸入二進制數,插拔電路闆等實作,這種“程式設計”很容易出錯,每個指令都是通過查指令表實作”。

比如要執行21号中斷,需要查表,得到21号中斷的指令就是CD 21。這樣不管你通過什麼方式,在記憶體指令位置,寫入兩個位元組,一個是CD(這可不是音樂CD光牒,而是二進制數,轉成十進制就是205),另一個是21(同樣是十六進制,十進制是33)。

什麼是彙編語言?

“既然是通過“查表”實作的,那當然也可以讓計算機來代替人查表實作了。于是就産生了彙編語言”,彙編語言産生的重要目的就是用容易記的符号來代替容易出錯的二進制數(或十六進制數)。

比如21号中斷,機器語言是CD 21。而彙編語言就規定中斷用int表示(interrupt的前三個字母),21号中斷就成了int 21h。其中21後面的h表示是表示這個21是十六進制。由于大小寫不敏感,是以int 21h寫成下列方式都等價:

int 33 Int 21h INT 21H

彙編指令集:

資料傳輸指令

它們在存貯器和寄存器、寄存器和輸入輸出端口之間傳送資料

  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.

  1. 标志傳送指令.

    LAHF 标志寄存器傳送,把标志裝入AH.

    SAHF 标志寄存器傳送,把AH内容裝入标志寄存器.

    PUSHF 标志入棧.

    POPF 标志出棧.

    PUSHD 32位标志入棧.

    POPD 32位标志出棧.

二、算術運算指令

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中去)

三、邏輯運算指令

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

四、串指令

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時重複.

五、程式轉移指令

無條件轉移指令 (長轉移)

JMP 無條件轉移指令

CALL 過程調用

RET/RETF過程傳回.

條件轉移指令 (短轉移,-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 清中斷允許位.

六、僞指令

DW 定義字(2位元組).

PROC 定義過程.

ENDP 過程結束.

SEGMENT 定義段.

ASSUME 建立段寄存器尋址.

ENDS 段結束.

END 程式結束.

寄存器在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。這樣作的好處是你可以更靈活地控制這個寄存器。

沒讓你一下學會,(其實有些永遠也不會似乎也不是什麼大不了的事)把其中的指令挑幾個重點的,你必須要記住,其它的慢慢學吧。

1資料傳輸指令。

mov A,B

這個指令是把B中的資料複制給A,(B中仍儲存原狀)。這裡的A和B可以是寄存器,可以是記憶體。可以同時是寄存器,但不能同時是記憶體。比如

mov ax,100 ;這是對的,注意100在這裡叫立即數,但這個數在編譯系統編譯成exe的時候儲存在記憶體中。

學過别的進階語言,你就可以了解為這就是指派語句 Let ax=100/ax:=100;/ax=100。

2 僞指令

僞指令不是真的指令,但它同時又是指令。之是以說這樣沖突的話,是因為僞指令不是機器語言的一部分,而是彙編語言的一部分,是你告訴彙編的編譯系統如何去作。

上面一行指令中,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

彙程式設計式架構

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。這三個段分别存資料,附加資料,代碼段。

編寫我們的Hello,world思路。

開始編寫我們的第一個程式。

程式要求:顯示一個“Hello,ZHENGQIAN.”怎麼樣?

思路:

1 要顯示一個字元串,根據前面我讓你們記的七八個指令夠嗎?答案是:不僅夠,而且還用不完。

首先定義一下總可以吧。

最後的

$

不要忘了。

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你随便寫什麼值影響都不大。

程式實作

data SEGMENT

msg DB ‘Hello, ZHENGQIAN.$’

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

編譯運作

把上面程式儲存成helloZHENGQIAN.asm後,就可以編譯運作了。

進入DOS,進入彙編目錄:

D:\debug>masm helloZHENGQIAN.asm

Microsoft ® Macro Assembler Version 5.00

Copyright © Microsoft Corp 1981-1985, 1987. All rights reserved.

Object filename [helloZHENGQIAN.OBJ]:

Source listing [NUL.LST]:

Cross-reference [NUL.CRF]:

50408 + 415320 Bytes symbol space free

0 Warning Errors

0 Severe Errors

上面連續三個回車,表示我要的都是預設值。下面是零個警告,零個嚴重錯誤。(當然了,我的程式還敢錯嗎?)

D:\debug> link helloZHENGQIAN

Microsoft ® Overlay Linker Version 3.60

Copyright © Microsoft Corp 1983-1987. All rights reserved.

Run File [HELLOZHENGQIAN.EXE]:

List File [NUL.MAP]:

Libraries [.LIB]:

LINK : warning L4021: no stack segment

三個回車仍要預設,後面有個警告,沒有棧段,這個沒關系,沒有的話系統會自動給一個

D:\debug>helloZHENGQIAN

Hello, ZHENGQIAN.

說明:運作成功。

D:\debug>

深度思考

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

編譯後仍然可以。

2 我編的程式在記憶體中是什麼樣子的呢?

D:\debug >debug ZHENGQIAN.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, ZHENGQIAN.$.

141F:0010 B8 1F 14 8E D8 8D 16 00-00 B4 09 CD 21 B8 00 4C …………!..L

-q

D:\debug >

上面是什麼呀?,還記得前面說的嗎?

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, ZHENGQIAN.$ —–>這是段位址裡的記憶體

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指令,串操作指令以及無操作數的指令,而且都比較簡單,自行總結。

一詞:excursion 遊覽,涉足

看完這篇文章就不要再問我彙編了

繼續閱讀