Assembly 为什么这个代码循环16次?

Assembly 为什么这个代码循环16次?,assembly,x86,Assembly,X86,REACH循环将重复多少次 mov bx,0 mov cx,0 again: shr cx,1 inc bx loop again 答案是16,但为什么 当我们有shl而不是shr时,答案是:无限循环。为什么?理解这样的问题有两种基本方法: 仔细思考并“执行”头脑中的代码。考虑每个指令意味着什么,以及它将如何影响代码的流动。 在编辑器中键入代码,组装代码,然后在调试器下运行。使用调试器单步执行代码,观察寄存器的值及其整体行为 请注意,这些基本上是相同的事情;一个是让机器完成所有的工作

REACH循环将重复多少次

mov bx,0 
mov cx,0 
again: 
shr cx,1 
inc bx 
loop again
答案是16,但为什么


当我们有
shl
而不是
shr
时,答案是:无限循环。为什么?

理解这样的问题有两种基本方法:

  • 仔细思考并“执行”头脑中的代码。考虑每个指令意味着什么,以及它将如何影响代码的流动。
  • 在编辑器中键入代码,组装代码,然后在调试器下运行。使用调试器单步执行代码,观察寄存器的值及其整体行为
  • 请注意,这些基本上是相同的事情;一个是让机器完成所有的工作,你只是看着魔法发生。如果您对汇编语言比较熟悉,那么在像这样的简单情况下,方法#1将是您所需要的全部。如果您只是在学习,但对它的感觉还不太清楚,那么“作弊”并让调试器提供帮助是非常有用的

    由于堆栈溢出上没有可用的调试器(尽管我仍然鼓励您自己尝试此方法),让我们使用我们的大脑求助于手动方法:

    显然,这首先将
    bx
    cx
    寄存器初始化为0

    请注意,没有一个真正的汇编程序员会像这样编写代码。他们总是写信

    xor bx, bx
    xor cx, cx
    
    这具有相同的效果(任何数字XOR本身都是0),但速度更快,使用的代码字节更少

    在循环内部,此代码运行:

    SHR
    将目标操作数(在本例中为
    cx
    )右移源操作数(在本例中为常量
    1
    )。回想一下,右移相当于除以2,但速度更快。因此,这与
    cx=cx>>1
    cx=cx/2
    相同

    INC
    将其操作数递增1。它与
    bx=bx+1
    相同

    在x86汇编编程中,
    循环
    指令不再广泛使用,因为它相对较慢。大概只有在小代码比快速代码更重要的情况下才会使用它。因此,我必须查看它的功能。您可以通过谷歌搜索助记符名称和“x86”来找到这些信息,也可以在英特尔的手册中找到,也可以在tag wiki的资料中找到,也可以在学习汇编语言编程所用的任何教科书/课程资料中找到。我查了一下,这是一个包含英特尔手册在线抄本的网站,是用谷歌找到的

    结果是,
    循环
    指令递减计数寄存器(
    cx
    ),如果
    cx!=0
    。否则,如果
    cx==0
    ,它将停止循环并失败

    有了这些知识,你应该能够在头脑中“执行”代码。bx上发生的事情与此无关。唯一有趣的操作是那些影响
    cx
    的操作。有趣的是,由于
    cx
    从0开始,并且
    0>>1
    是0,因此
    循环
    指令将其递减1,使其在循环的第一次迭代后为-1

    对有符号值使用按位右移有点不寻常(通常是一个bug),但它在x86汇编中是一个定义良好的操作(与C或C++中不同)。基本上,发生的情况是移位的位(在本例中,最后一位)消失,空位槽被零填充。所以,右移1只会在最高有效位上放置一个0。(从技术上讲,它会将移位的位放在进位标志中,但这在这段代码中并不重要,因为没有任何东西在测试进位标志。)

    例如,
    6>>1==3
    0000 0110>>1==0000 0011


    现在,您应该充分理解该行为,以便能够回答第二个后续问题。但是如果你需要一个提示。它是按位左移,相当于2的乘法


    在这种情况下,代码将无限循环,因为
    cx
    永远不会为0,这是
    loop
    指令正在检查的条件!为什么会这样?因为
    LOOP
    首先递减
    cx
    ,然后测试它是否为零。正如Peter Cordes所指出的,也许更好的思考方式是
    cx
    是否可以是1。
    SHL
    指令确保永远不会出现这种情况,因为
    SHL
    之后的
    cx
    的结果是0(在这种情况下
    LOOP
    将把它减少到-1,从而继续循环),或者>=2(在这种情况下
    LOOP
    将把它减少到>=1,从而继续循环).

    理解这样的问题有两种基本方法:

  • 仔细思考并“执行”头脑中的代码。考虑每个指令意味着什么,以及它将如何影响代码的流动。
  • 在编辑器中键入代码,组装代码,然后在调试器下运行。使用调试器单步执行代码,观察寄存器的值及其整体行为
  • 请注意,这些基本上是相同的事情;一个是让机器完成所有的工作,你只是看着魔法发生。如果您对汇编语言比较熟悉,那么在像这样的简单情况下,方法#1将是您所需要的全部。如果您只是在学习,但对它的感觉还不太清楚,那么“作弊”并让调试器提供帮助是非常有用的

    由于堆栈溢出上没有可用的调试器(尽管我仍然鼓励您自己尝试此方法),让我们使用我们的大脑求助于手动方法:

    显然,这首先将
    bx
    cx
    寄存器初始化为0

    请注意,没有一个真正的汇编程序员会像这样编写代码
    xor bx, bx
    xor cx, cx
    
    shr cx,1 
    inc bx 
    
    loop again