MASM匯編中偽指令ASSUME的作用
??在學習16位MASM宏匯編時,開始犯的一個錯誤就是認為assume就是給CS、SS、DS等段寄存器進行初始化賦值的。但又需要在源碼的開始階段,通過mov ax,datasg和mov ds,ax對DS進行手動賦值,assume ds:datasg不是已經賦值過了么,倍感疑惑!
??比如下面代碼:
assume cs:codesg,ss:stacksg,ds:datasg1,es:datasg2
;-------------------------------------------
stacksg segment stack
db 100 dup (0)
stacksg ends
;-------------------------------------------
datasg1 segment
tnum1 db 6
datasg1 ends
;-------------------------------------------
datasg2 segment
tnum2 db 9
datasg2 ends
;-------------------------------------------
codesg segment
start:
mov ax,datasg1
mov ds,ax
mov ax,datasg2
mov es,ax
;-------------------------------------------
mov al,tnum1
mov al,tnum2
;-------------------------------------------
mov ax,4c00h
int 21h
codesg ends
end start
??一、偽指令的概念
??偽指令就是編譯器(masm)將匯編代碼編譯成機器碼時,為編譯器提供信息,本身并不生成相應的機器碼。
??二、尋址的概念
??就是cpu去哪里獲取數據或指令,換句話就是說每一個含有操作數的匯編指令,這些操作數如果含有標號(示例中:start:)、變量名(示例中:tnum1)等,這些符號都代表著什么。
??1、這些符號都代表的是地址,即去這些地址獲取數據。示例中的mov ax,tnum1就會編譯成mov ax,[0000],cpu會去偏移地址0000處獲取數據。
??2、16位的程序都是通過段地址(segment address):偏移地址(offset address)的方式計算地址,但許多指令中并不體現段地址,而是通過默認的方式進行指定,如jmp的默認段地址是cs;push和pop的默認段地址是ss;mov的默認段地址是ds。
??三、編譯器的地址解析
??編譯器(masm)其中一個作用就是把變量或標號解析成地址,一般都是偏移地址,如示例中的變量tnum1解析成[0000]。談到偏移地址,對應的肯定就有段地址。這個[0000]對應的是哪個段,不同段的偏移地址[0000]處的內容并不相同,如datasg1偏移地址[0000]處的值就是6;datasg2偏移地址[0000]處的值就是9。mov al,tnum1或mov al,tnum2對應的執行碼都是mov ax,[0000],只是段地址不相同,CPU是獲得正確的值呢,請看示例代碼編譯后debug反匯編的代碼:
??
??示例中通過修改默認段寄存器進行區分(機器碼26),編譯器幫我們把位于datasg2段內的數據前加上了ES段前綴,CPU在執行這段代碼時就會通過ES:OFFSET(tnum2)的方式尋址。這時ES的值是不是執行datasg2,編譯器和CPU都不管,因此,執行這段代碼(ES: mov al,[0000])前,要手動賦值(mov ax,datasg2 mov es,ax)。
??當然也可以不修改默認段寄存器,通過修改DS的值,即在mov al,tnum2前添加mov ax,datasg1和mov ds,ax的方式定位tnum2。
??四、ASSUME的作用
??assume就是masm在編譯代碼時,如果遇到需要確定段寄存器的代碼時,根據assume的設定(段與段寄存器的對應關系),生成該情況下對應的代碼。官方文檔:Subsequent instructions that assume a default register for referencing labels or variables automatically assume that if the default segment is segmentregister, then the label or variable is in the name segment or group 。
??默認情況下,編譯器會將各個段中的變量解析成與該段開始地址對應的偏移地址,即tnum1對應段datasg1的偏移地址是[0000];tnum2對應段datasg2的偏移地址也是[0000]。
??如果沒有assume,masm按默認情況處理,只生成偏移地址,本示例則生成的機器碼相同。CPU在執行這2處指令時,則CPU都是去DS的偏移地址[0000]處獲取數據,顯然會出錯。
??如果添加assume偽指令,masm按assume的假設,把datasg1地址對應著ds,因tnum1位于datasg1段,該處不用調整。把datasg2對應著es,因tnum2位于datasg2段,該處需修改默認段寄存器,即添加機器碼26。
CPU執行到此處,執行mov指令前,現將默認寄存器改為ES。
??五、手動添加段前綴(segment-override operator)
??如果不添加assume,手動添加段前綴也能達到相同效果,但每句都要添加。如示例mov al,tnum1默認的ds,如果ds的值是datasg1的地址,則正確執行。mov al,tnum2,如es的值是datasg2的地址,則改成:mov al,es:tnum2。
??注意:這里也可以將mov al,tnum2改成mov al,ds:tnum2,則偏移地址是相對于datasg1的開始地址的,也沒問題(當然如果這樣,就沒必要分段啊)。
??六、初始化段寄存器
??前面的操作都是以ds的值是datasg1的地址,es的值是datasg2的地址為前提的,assume和masm都是在這個的前提下進行偏移地址解析的。但在執行階段,ds和es的值是不是對應的段地址,cpu無法確定,所以需要在代碼最前面對ds和es進行手動初始化(如示例)。為什么ds的值和es的值,不
