指针的概念在前面的章节里已有所讲述,这个词诈听上去有些悬妙,其实要用最明确的话来说,指针就是数据在内存中的地址。这个地址可以是单纯的16位偏移地址,也可以是32位的"段:偏移"形式。
我们前面编制的许多程序中都应用了"OFFSET"这个分析运算符,实际上这个分析符起的作用就是用于取得指向数据块首的指针。但是使用"OFFSET"并非是唯一的方法,请看下面的程序所指出的另一条途径:
data segment assume ds:data msg db World Wild Wed--Paradise and Tomb',0 vram dd 0b8000670h ;采用DD伪指令直接定义32位指针 data ends code segment assume cs:code main proc far push ds ;初始化堆栈 xor ax,ax push ax mov ax,data ;初始化DS寄存器 mov ds,ax mov ax,0003h ;设置字符显示模式3 int 10h lea si,msg ;将字符串MSG的有效地址送入SI寄存器 les di,vram ;将显示缓存的指针送入ES:DI mov ah,2 ;字符属性字节 loop1: lodsb ;取得一个字符 cmp al,0 ;已经到字符串结尾了吗? jz exit ;已到字符串结尾,转EXIT结束 stosw ;将字符及属性字节送入显示缓存中 jmp loop1 ;转LOOP1继续显示下一个字符 exit: mov ah,0 ;等待键盘输入 int 16h ret ;结束进程 main endp code ends end main
这个程序中引入了一些新的指令和伪指令,取代了"OFFSET"的指令就是"LEA":
助记符:LEA(Load Effective Address)
用 途:将数据的有效地址送入指定寄存器
格 式:LEA 寄存器,数据标号
执 行:标号对应的偏移地址送入指定的寄存器中
"有效地址"这个词听上去有些耳生,其实有效地址就是数据在内存某个段内的偏移地址。使用LEA指令时不能再用"OFFSET"分析运算符,否则反而有错误。这个指令执行的操作与指令"MOV 寄存器,OFFSET 数据标号"完全一致,但是它们之间有一些很微妙的差别。这个差别在应用DEBUG编制程序时就有体现,请看下面的例子:
C:\ASM\>DEBUG[Enter] -a100[Enter] 0F6A:0100 mov ah,9 0F6A:0102 mov dx,109 0F6A:0105 int 21 0F6A:0107 int 20 0F6A:0109 db'This is a sample',0d,0a,24 0F6A:011C[Enter] -a100[Enter] 0F6A:0100 mov ah,9 0F6A:0102 lea dx,[10a] 0F6A:0106 int 21 0F6A:0108 int 20 0F6A:010A db'This is a sample',0d,0a,24 0F6A:011D [Enter]
这两个程序很简单,可以在屏幕上显示一个字符串。所不同的是第二个程序换用"LEA"指令完成取偏移地址的功能。"LEA"的应用格式是很特殊的,表面上看这个指令采用一个存储器直接寻址方式,取到DX寄存器中的数据应该是"T"与"h"的ASCII码6854H,实际上如果跟踪执行这个程序就会观察到取到DX寄存器中的数据并非是6854H,而正是010AH。可见这个指令与MOV指令并不相同,通常对"寻址方式"的理解并不适用于LEA指令。
由此看来指令"LEA BX,[SI]"也不是要把内存中[SI]地址处的两字节数据送进BX寄存器,而是送进SI的值。这一点可以说是这个指令最奇怪的地方,不过要是再细想一下这也正是将LEA称为"取有效地址指令"的原因。
特别值得一提的是Borland公司出品的Turbo Assembly编译程序(TASM.EXE)对LEA指令的处理方法很不寻常,它会不声不响地用指令"MOV 寄存器,OFFSET 标号"替换掉LEA。原因很简单,这样替换之后生成的可执行程序会短小一些。这一点从刚才我们用DEBUG编制的两个对比程序中也可以看出来。
另外新增的LES指令是用于处理32位指针的:
助记符:LES(Load ES with pointer)
用 途:将存于内存中的32位指针送入ES和指定的寄存器内
格 式:LES 寄存器,存储单元
执 行:32位指针中低16位作为偏移地址送入指定的寄存器中,高16位作为段地址送入ES寄存器中