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的值,不在masm编译阶段确定下来呢,而是要在执行阶段手动赋值?这涉及到可执行程序的装载和地址转换,datasg1和datasg2的地址在执行阶段才能确定,而且每次执行并不相同,有兴趣可以查阅相关资料。但无论段地址最终是什么,但偏移地址始终不变。

  那为什么cs和ss的段地址却不用手动初始化呢,难道他们的地址在masm编译阶段就确定了?

  cs和ss的段地址值保存在exe头部,需要在执行阶段确认,通过与加载时确定的偏移地址确认,无需手动初始化,系统自动帮你初始化了。

  ip的值保存在在exe头部,通过end [address]伪指令来确定的。

  sp的值保存在exe头部,通过声明segment stack确定,sp的值通过stack段的大小确定。

  段datasg1的地址能否在代码中就确定下来呢,答案是可以的,定义时用at,如datasg1 segment at 4de9。

  注意:关于段寄存器的初始化,本段描述的并不准确,如需详细了解,请参考博主关于exe加载及重定位的博文。

  七、ASSUME伪指令的取消

  assume可放在代码的任何位置,只影响到assume之后的代码编译,直至代码最后。如希望中途调整或取消,可填写assume cs:nothing或全部取消assume nothing。

  八、补漏 

  当我们使用段前缀不是寄存器名称,而是段名称如:datasg或group名称如dgroup时,一定要使用assume将该段名称或组与段寄存器关联,否则编译器会忽略该段前缀。

mov ax,dgroup:var1
mov ax,datasg:var2

  九、综合示例,显示hello,world!!

assume ds:datasg,ss:stacksg
stacksg segment stack
    db 256 dup(?)
stacksg ends
assume cs:datasg
datasg segment
    msg: 
    db 'Hello World!!$'
datasg ends


assume cs:codesg
codesg segment
main proc
    mov ax,datasg
    mov ds,ax

;forward-referenced,must far ptr call
    call far ptr greed
    
    mov ax,4c00h
    int 21h
    
main endp
codesg  ends

assume cs:greedsg
greedsg segment
greed proc far
    mov ah,9
    mov dx,OFFSET msg
    int 21h
    ret
greed endp
greedsg ends

end main
posted @ 2023-08-30 18:54  美洲象  阅读(191)  评论(0编辑  收藏  举报