【问题标题】:TASM program outputs garbage and may hang upon exitTASM 程序输出垃圾并可能在退出时挂起
【发布时间】:2016-01-06 16:01:25
【问题描述】:

我有以下代码,它接受一个十六进制格式的数字并打印出它的十进制格式。

这个程序在 Turbo Debugger 中运行得还不错,但是当我在 DOS 中运行它时,我在输出数字后看到一些额外的符号:

.model small
.stack 100h
.486
.code

save proc
    itm:
        xor dx,dx
        mov bx,10
        div bx
        add dl,30h
        inc cx
        pop bx
        push dx
        push bx
        cmp ax,0
        jne itm
   ret
save endp

start:
    mov ax, 0FFFFh
    call save
    mov ah, 02
    print:
        pop dx
        int 21h
        loop print
       int 20h
end start

输出:

C:\TASM>lab31 65535 ò Φ■╤  9°☻░╧♠UWSîÄPA÷0ó♥┴∞└εê$♦ó♥α♦▲ê$ó♦Σê←ë♦ó♥☻╨sÄ÷♣t╣ ╞ ÷┤ⁿ8sê¬ê²≤&mî│░ⁿ┘╗♥÷ t<sÿ☻╪╟♣I☼>♥b!├─4&Gê&_ëΩî∞[┴éΦ z│Φ ☺\│Φ ♀fδ[♥3¡ïA1èG┴├═≥uè ç♦└┌é─Ω╕↕ëX╪♥♦♫↕Y^▼Z╖ ←tÇ5▲♦▼δá♦├☻├ █ ☻┬! C└(A∞1▬:↕ÿ├ƒ♥╞[%█☼C└≥░Φ 1357                46$♦♦

如您所见,65535 打印正常,但随后出现垃圾。当我在 Turbo Debugger 中运行程序时,它在写出 65535 后挂起。

【问题讨论】:

  • 函数2 of int 21h 打印一个字符,而不是一个数字。您必须在打印前转换为字符串。
  • @owacoder 他正在打印字符。
  • 我在您的代码中看到的一个问题是我看不到 CX 设置为零的位置(您将其用作字符计数器,但似乎没有对其进行初始化)

标签: assembly dos x86-16 tasm real-mode


【解决方案1】:

您的代码存在一些问题。您使用 CX 作为字符计数器,但是在此代码中您无需初始化即可递增:

save proc
    itm:
        xor dx,dx
        mov bx,10
        div bx
        add dl,30h
        inc cx                 ; increment CX, without initialization
        pop bx
        push dx
        push bx
        cmp ax,0
        jne itm
   ret
save endp

要解决此问题,您可以通过在 save 过程中的主循环之外将其初始化来将 CX 设置为零:

save proc
        xor cx,cx              ; Explicitly clear CX counter.            
    itm:
        xor dx,dx

要查看加载 EXE 时有关寄存器状态的信息,请参阅本文底部的 register contents at program entry 部分:

Register  Contents
AX        If loading under DOS: AL contains the drive number for the first FCB 
          in the PSP, and AH contains the drive number for the second FCB.
BX        Undefined.
CX        Undefined.
DX        Undefined.
BP        Undefined.
SI        Undefined.
DI        Undefined.
IP        Initial value copied from .EXE file header.
SP        Initial value copied from .EXE file header.
CS        Initial value (relocated) from .EXE file header.
DS        If loading under DOS: segment for start of PSP.
ES        If loading under DOS: segment for start of PSP.
SS        Initial value (relocated) from .EXE file header.

Register CX 被认为是未定义的(BP,BX,DX,SI,DI) 在进入您的 EXE 程序时。 AX 可能不为零,因为它用于将信息传递给您的 EXE。因为 CX 是未定义的,它可能包含也可能不包含 0。

有时,当您针对可执行文件运行调试器时,CX 的值可能与不使用调试器运行时不同。在 Turbo Debugger 的情况下,CX 在进入时似乎为零。如果您在调试器之外运行程序,它可能不为零,并且会导致您遇到的问题。我建议在使用通用寄存器之前对其进行初始化。

至于程序结束时的挂起是因为您使用的是int 20h。您的代码表明您正在生成 .EXE 文件(不是 .COM)。退出.EXE 程序的典型方法是使用int 21h,其中AH=04chAL 是您的退出代码。如果您将int 20h 替换为以下代码,它将退出并返回值为0:

   mov ax, 4c00h
   int 21h

使用 TASM,您还可以使用 .exit ndirective(n = 退出返回值)作为替代。这应该生成适当的汇编代码以退出回到 DOS。

int 20h 经常在 .COM 程序中使用(retn 更常见)。 int 20h 依赖于 CS:0 作为 PSP 块的地址。这在 .COM 程序中默认是正确的,但在 .EXE 程序中不是这样。有关这方面的更多信息可以找到herehere

Int 21h 函数 4Ch

   Notes:         It is best to use INT 21h Function 4Ch to exit from
                  '.exe' programs since Function 4Ch doesn't require
                  that CS point to the PSP.

国际 20 小时

   Notes:         This function is an historical remnant offering no
                  advantages over Function 4Ch. It's better to use
                  function 4Ch, which returns an error code that other
                  programs can access (via Function 4Dh or the
                  ERRORLEVEL statement in batch files); also, CS need
                  not be set first when using Function 4Ch. (INT 20h
                  is equivalent to Function 0.)

【讨论】:

  • 很高兴看到您的回答。我投了赞成票,因为你应得的。我不接受别人的工作(你在我的回答中暗示我用 cmets 做的)。
  • @owacoder 我等了一会儿,然后发现你在等我 ;-)。实际上,我并没有在您的回答中暗示任何有关 cmets 的内容。我实际上并不像其他人一样关心声誉点(并且感谢您在回答中提到我)。您经常会发现我将对琐碎问题的回答作为评论而不是答案,如果出现的答案可能与我的 cmets 相似,则没有问题。
  • 谢谢,我接受这个答案,因为程序现在可以正常工作。关于int 20h 的注意事项我很清楚。但是,我不明白cx 的事情。在调试器中,我看到程序启动时 CX 设置为零。为什么要显式设置?
  • @ka2m : CX 被认为是未定义的(BPBXDX也是如此>、SIDI) 进入您的 EXE 程序。因为它是未定义的,所以它可能包含也可能不包含 0。有时当您针对可执行文件运行调试器时,CX 的值可能为零,如果在调试器之外运行,它可能不为零。它真的可以是任何东西。要查看有关加载 EXE 时寄存器状态的信息,请参阅此 article 底部的 在程序入口处注册内容部分
【解决方案2】:

看起来循环print 继续超过数字的末尾,因为您没有事先将CX 初始化为零。 (感谢@MichaelPetch,我在他的评论出现的同时发现了这一点)我会将您的save 例程更改为如下所示:

save proc
    xor cx,cx
    itm:

【讨论】:

  • 虽然这解决了部分问题,但 OP 确实提到他们的程序也挂起,那是因为他在 EXE 中使用了int 20h。虽然您可以通过更改 CS 使其在 EXE 中工作,但通常首选将 int 21hah=04ch 和 al 返回码。 int 20h 更常见于 COM 程序(尽管有一个简单的 retn 也可以)
  • @MichaelPetch - 为什么不发布答案?
  • 因为你已经有了答案。对我来说添加一个新的并复制它并添加一个新的部分毫无意义。我不关心声誉点,所以谁得到它们并不重要。如果可以使用一个答案来回答这两个问题,我宁愿看到它而不是 2 个单独的答案。
  • 如果您有进一步的见解,发布另一个答案不会“毫无意义”。
猜你喜欢
  • 1970-01-01
  • 2018-07-23
  • 1970-01-01
  • 2014-12-18
  • 1970-01-01
  • 2018-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多