【问题标题】:LOOP is done only one time when single stepping in Turbo Debugger在 Turbo Debugger 中单步执行时,LOOP 仅执行一次
【发布时间】:2018-06-06 22:59:52
【问题描述】:

代码必须输出'ccb',但只输出'c',LOOP只做一次,我在TD里校准过,为什么LOOP只做一次?

我认为我必须减少 STRING_LENGTH,所以我写了

DEC STRING_LENGTH

但它不起作用,所以我是这样写的

MOV SP,STRING_LENGTH
DEC SP
MOV STRING_LENGTH,SP

我知道你现在在想什么,这太不正确了,你是对的)))

我可以使用 C++,但我只想在汇编中使用,

DOSSEG
.MODEL SMALL
.STACK 200H
.DATA
STRING DB 'cScbd$'
STRING_LENGTH EQU $-STRING
STRING1 DB STRING_LENGTH DUP (?) , '$'
.CODE
MOV AX,@DATA
MOV DS,AX
XOR SI,SI
XOR DI,DI

MOV CX,STRING_LENGTH

S:

MOV BL,STRING[DI]
AND STRING[DI],01111100B
CMP STRING[DI],01100000B
JNE L1
MOV AL,BL
MOV STRING1[SI],AL
ADD SI,2
L1:

ADD DI,2

LOOP S

MOV DL,STRING1
MOV AH,9
INT 21H
MOV AH,4CH
INT 21H
END

【问题讨论】:

  • 更有可能你的循环没有按照你的想法做。您不会在循环中破坏 CX,因此它应该运行 STRING_LENGTH 次。使用调试器单步执行并查看最终在内存中的结果。无论如何,您希望循环做什么?还有你为什么要毁掉原来的STRING
  • 我用调试器单步执行,看到在 MOV CX,STRING-LENGTH //CX IS 0006 after LOOP S //CX IS 0000 但是为什么?
  • 在 Turbo Debugger (TD.EXE) 中,“F8 步”将完全“执行”loop 指令(cx 多次,或者更多,如果代码同时修改cx )。使用“F7 跟踪”选项从 cx=6 变为 cx=5。 (投赞成票是因为您正在尝试调试,在发布问题后继续沟通,并且 TD UI 在这种特殊情况下有些棘手,但除此之外您还有很多工作要做)。
  • 在循环内部,当它写入 string1 时,它写入一个字节,但将 si 增加 2。
  • 循环迭代 6 次,每次将 di 递增 2,因此它会超出输入字符串的末尾,并在执行过程中覆盖。

标签: debugging assembly x86-16


【解决方案1】:

在 Turbo Debugger (TD.EXE) 中,F8“F8 步骤”将完全执行loop,直到cx 变为零(您甚至可以通过更新@987654323 创建无限循环@ 回到某个值,防止它到达 1 -> 0 步)。

要从 loop 指令中获得“单步”指令,请使用 F7“F7 跟踪” - 这将导致 cx 从 6 变为 5,并且代码指针将跟随循环开始处的跳转。


关于您的代码的其他一些问题:

MOV SP,STRING_LENGTH
DEC SP
MOV STRING_LENGTH,SP

sp不是通用寄存器,不要这样计算。每当某些指令确实隐式使用堆栈 (push, pop, call, ret, ...) 时,值就会在由ss:sp 寄存器对寻址的内存区域中写入和读取,因此通过操作sp 值,您正在修改当前的“堆栈”。

同样在 16 位 x86 实模式下,所有中断(键盘、定时器、...),当它们发生时,标志寄存器的当前状态和代码地址被存储到堆栈中,然后将控制权交给中断处理程序代码,这通常会将附加值推送到堆栈中,因此内存中低于当前ss:sp 的地址在 16 位 x86 实模式下是不安全的,并且内存内容通过同时执行的所有中断保持“随机”变化(TD.EXE 本身在每一步之后都会使用此堆栈内存的一部分)。

对于算术使用其他寄存器,而不是sp。一旦你对“堆栈”有足够的了解,你就会明白什么样的sp 操作是常见的以及为什么(比如sub sp,40 在函数开头需要额外的“本地”内存空间),以及如何将堆栈恢复回预期状态。


还有一件事:

MOV SP,STRING_LENGTH
DEC SP
MOV STRING_LENGTH,SP

STRING_LENGTH 是由EQU 定义的,这使得它的编译时间为常数,并且只有编译时间。它不是“变量”(内存分配),与 someLabel dw 1345 之类的东西相反,这会导致汇编器发出两个字节,其值为 0100_0001B, 0000_0101B(当以小端方式读取为 16 位字时,该值是 1345 编码的) ,并且第一个字节地址具有符号名称someLabel,可以在进一步的指令中使用,例如dec word ptr [someLabel],在运行时将内存中的值从1345递减到1344。

EQU 不同,它赋予符号STRING_LENGTH 最终值,如14。

所以你的代码可以读作:

mov sp,14   ; makes almost sense, (practically destroys stack setup)
dec sp      ; still valid
mov 14,sp   ; doesn't make any sense, constant can't be destination for MOV

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-02
    • 2023-04-07
    • 2013-11-15
    • 1970-01-01
    • 1970-01-01
    • 2018-01-30
    • 2014-02-08
    • 1970-01-01
    相关资源
    最近更新 更多