ITEEDU

下面给出此程序的反汇编形式,注意指令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文件:

TEMP.ASM
                
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之间的关系,将在下面一节中详细讲解。