下面给出此程序的反汇编形式,注意指令MOV AL,NOTE[BX]被编译成MOV AL,[BX+0012],这和我们在第三章中编制的PROG3-A中的用法一样,由于标号NOTE本身就表示数组的起始地址,因此这条指令的源程序也可写成"MOV AL,[BX+NOTE]。"
C:\ASM\>DEBUG MUSIC.COM[Enter] -u0 50[Enter] 0A53:0000 B84E0A MOV AX,0A4E 0A53:0003 8ED8 MOV DS,AX 0A53:0005 B0B6 MOV AL,B6 0A53:0007 E643 OUT 43,AL 0A53:0009 BB0000 MOV BX,0000 0A53:000C 8A871200 MOV AL,[BX+0012] 0A53:0010 8AA72D00 MOV AH,[BX+002D] 0A53:0014 43 INC BX 0A53:0015 53 PUSH BX 0A53:0016 50 PUSH AX 0A53:0017 98 CBW 0A53:0018 D1E0 SHL AX,1 0A53:001A 8BD8 MOV BX,AX 0A53:001C 8B870000 MOV AX,[BX+0000] 0A53:0020 E642 OUT 42,AL 0A53:0022 8AC4 MOV AL,AH 0A53:0024 E642 OUT 42,AL 0A53:0026 E461 IN AL,61 0A53:0028 0C03 OR AL,03 0A53:002A E661 OUT 61,AL 0A53:002C B400 MOV AH,00 0A53:002E CD1A INT 1A 0A53:0030 58 POP AX 0A53:0031 8AC4 MOV AL,AH 0A53:0033 98 CBW 0A53:0034 03C2 ADD AX,DX 0A53:0036 8BD8 MOV BX,AX 0A53:0038 B400 MOV AH,00 0A53:003A CD1A INT 1A 0A53:003C 3BDA CMP BX,DX 0A53:003E 75F8 JNZ 38 0A53:0040 E461 IN AL,61 0A53:0042 24FC AND AL,FC 0A53:0044 E661 OUT 61,AL 0A53:0046 5B POP BX 0A53:0047 80BF1200FF CMP BYTE PTR [BX+0012],FF 0A53:004C 75BE JNZ 000C 0A53:004E B44C MOV AH,4C 0A53:0050 CD21 INT 21 -da4e:0 0A4E:0000 00 00 C8 11 BD 0F 21 0E-57 0D E2 0B 96 0A 6F 09 ......!.W.....o. 0A4E:0010 E7 08 03 02 01 02 03 03-03 02 02 02 03 05 05 03 ................ 0A4E:0020 02 01 02 03 03 03 03 02-02 03 02 01 FF 04 04 04 ................ 0A4E:0030 04 04 04 08 04 04 08 04-04 08 04 04 04 04 04 04 ................ 0A4E:0040 04 04 04 04 04 04 08 00-00 00 00 00 00 00 00 00 ................
仔细阅读反汇编出来的程序,可以看到CMP指令的反汇编结果和源程序有很大的不同:其一,源程序中并没有"BYTE PTR",这样的文字;其二,"-1"居然成了"FF",难道"-1=255"吗?
在回答这两个问题之前,我们先将下面给出的程序编译成COM文件:
code segment assume cs:code org 100h main proc far jmp short start ;跳过数据区 msg db 'Hello,World!',24h ;定义一个字符串 crlf dw 0d0ah ;定义回车、换行符 start: mov ah,9 ;选择DOS API的09H功能 mov dx,offset msg ;设置DX寄存器指向字符串首地址 int 21h ;显示字符串 mov ah,2 ;选择DOS API的02H功能 mov dl,crlf ;取得ASCII码0AH int 21h ;显示换行符 mov dl,crlf+1 ;取得ASCII码0DH int 21h ;显示回车符 int 20h ;结束程序 main endp code ends end main
程序并不复杂,先显示一个字符串,再分别显示"回车"和"换行",注意"回车"和"换行"是以"字"的形式定义在内存中的。那么程序又是如何应用这一个字的数据呢?
指令"MOV DL,CRLF"和"MOV DL,CRLF+1"清楚地反映了程序的意图:为适应DOS的要求,我们只能将这个"字"拆成"字节",每次取出八位数据。从表面上看程序并没有错误,但实际上这样的程序无法编译,下面是我用TASM编译这个程序时出现的错误:
C:\ASM\>TASM TEMP[Enter] Turbo Assembler Version 3.2 Copyright (c) 1988, 1992 Borland International Assembling file: temp.ASM **Error** temp.ASM(13) Operand types do not match **Error** temp.ASM(15) Operand types do not match Error messages: 2 Warning messages: None Passes: 1 Remaining memory: 459k
给出的错误信息告诉我们 "运算类型不等"。看来实际情况并非如想象中那样,编译程序比我们人类要"呆"得多。如何使程序正确编译呢?
一种方法,改用DB伪指令定义数据。将第七行改写成 "CRLF DB 0DH,0AH";另一种方法就是用"PTR"操作符。
PTR
这个操作符用于显式地指示操作数的类型,它的使用很简单:"type PTR 目的地址"
code segment assume cs:code org 100h main proc far jmp short start msg db 'Hello,World!',24h crlf dw 0d0ah start: mov ah,9 mov dx,offset msg int 21h mov ah,2 mov dl,byte ptr crlf int 21h mov dl,byte ptr crlf+1 int 21h int 20h main endp code ends end main
指令"MOV DL,BYTE PTR CRLF"明确地告诉编译系统程序要从地址"CRLF"处取得一个"BYTE"。
举这个例子只是为说明"PTR"操作符的用途,并非显示什么算法。说实在的,对于上面这个问题来讲最简单的方法就是将"CRLF DW 0D0AH"改为:"CR EQU 0DH"和"LF EQU 0AH"。这样可以缩减程序的长度,而且也不必使用"PTR"操作符。
理解了"PTR"操作符,我们就可以更深入的讨论一下ADD和SUB这两条指令,请考虑一下这样两条指令是否正确:"ADD BL,WORD PTR [0200]"、"SUB AX,BYTE PTR [3000]"。
先设想一个结论,再用DEBUG检验设想的结论,然后总结出这两条指令在应用时的注意事项。
至于-1和255之间的关系,将在下面一节中详细讲解。