通常情况下,一个正常的程序至少也要有代码段、数据段和栈段。下面我们学习如何在MASM中使用伪指令定义这三个段并使用。
assume cs:codeseg,ds:dataseg
dataseg segment
dw 0,0,0,0
dataseg ends
codeseg segment
start:
;设置数据段寄存器DS
mov ax,dataseg
mov ds,ax
mov cx,4
mov bx,0
loop1: mov word ptr ds:[bx],0ffffh
add bx,2
loop loop1
mov ax,4c00h
int 21h
codeseg ends
end start
该指令告诉MASM汇编器程序中定义的段。
例如assume ds:dataseg
,那么后面我们就可以用segment...ends
定义一个段了,而且我们还可以用mov ax,dataseg
的形式获得段的起始地址。
end伪指令一般和一个标签配合使用,通常写为start...end start
,其中start处为程序开始的地方,end为结束的地方。该信息存放在EXE的描述信息中,起始地址用于设置CS:IP寄存器,结束地址用于程序返回。
栈段的定义和数据段是一样的,这里就不多说了,有关栈的知识将在后续章节详细介绍。
assume cs:codeseg,ss:stackseg
stackseg segment
dw 0,0,0,0
stackseg ends
codeseg segment
start:
;设置栈段寄存器SS
mov ax,stackseg
mov ss,ax
mov sp,8
mov ax,0ffffh
push ax
pop bx
mov ax,4c00h
int 21h
codeseg ends
end start
虽然王爽的《汇编语言》书上栈段是如上方式定义的,但是我发现这样定义链接时会有一个警告:
LINK:warning L4021:no stack segment
我们明明定义了栈段,但是为什么还是会报出这个警告呢?因为EXE中本来可以存放栈段的地址,而我们使用的方式是通过汇编指令指定SS寄存器,手动指定栈段地址。
stackseg segment stack
dw 0,0,0,0
stackseg ends
使用上面代码的方式定义栈段,操作系统就可以根据EXE的描述信息给SS寄存器指定一个正确的值,SP则会自动设置到栈底的前一个元素,代码可以简化为下面的形式:
assume cs:codeseg,ss:stackseg
stackseg segment stack
dw 0,0,0,0
stackseg ends
codeseg segment
start:
;不再需要手动设置栈信息了
;mov ax,stackseg
;mov ss,ax
;mov sp,8
mov ax,0ffffh
push ax
pop bx
mov ax,4c00h
int 21h
codeseg ends
end start
当然,第一种方式比较通用,万一我们使用的操作系统的可执行文件格式不支持定义栈段地址,那么我们还是得手动指定。