我不明白什么指令“和ax,1111111100000000b”正在做什么

时间:2016-04-16 16:38:39

标签: linux assembly x86 nasm

我已经获得了一些代码,我理解了大部分代码,并知道会发生什么。我在理解这段代码时遇到了问题:

div bl
and ax, 1111111100000000b

我认为第一行只是一个简单的划分,但是and ax, 1111111100000000b做了什么?

完整的代码是:

section .data
number db  5
answer db  1
section .bss
section .text
    global _start
_start:
       mov esi, number
keith: mov eax, 0
       mov al, [esi]
       mov dl, al
       mov bl, 2
loopy: div bl         ; ax / bl with quotient in al and remainder in ah
       and ax, 1111111100000000b
       cmp ax, 0
       je  there
       inc bl
       cmp bl, dl
       je done
       mov eax, 0
       mov al, [esi]
       jmp loopy  
                     ; restore the  number back into
                     ; ax
there: mov byte[answer], 0
done:
       mov eax,1       ; The system call for exit (sys_exit)
       mov ebx,0       ; Exit with return code of 0 (no error)
       int 80h

2 个答案:

答案 0 :(得分:2)

我认为将它写成and ax, 0xFF00 会更清楚,因为计算8个1和8个0对于人类读者来说更难。

最好是xor al,al将低字节归零(但在写ax后读取完整al时会产生部分寄存器减速。)

实际上,代码只是检查剩余部分为零的非常愚蠢的方式。整个代码很漂亮。使用movzx加载一个字节,其高位字节为零,而不是mov eax,0 / mov al, [mem]

对于测试,只需test ah,ah / je there直接测试其余部分。如果余数为零,and ax也会设置ZF,因此and ax, 0xFF00 / jz there也是等效的。这只是糟糕的代码。

这是重写:

section .data
number   db  5
is_prime db  1             ; we store a zero if it's not prime

section .text
global _start
_start:
       movzx  edx, byte [number]  ; there was no need to put the pointer in a register
       mov    ecx, 2

;; This whole algorithm is very naive.  Slightly better: check for even (low bit), then only check odd divisors.  Google for stuff that's better than trial division.
.trial_division:
       mov    eax, edx   ; fresh copy of number.  cheaper than re-loading from cache
       div    bl         ; ax / bl.  quotient in al.  remainder in ah
       test   ah,ah      ; set flags based on remainder
       jz    .found_divisor

       inc    ecx
       cmp    ecx, edx    ; upper bound only needs to be sqrt(number), but we're aiming for small / simple code, not efficiency apparently
       jl    .trial_division  ; the final conditional branch goes at the end of the loop

       jmp   done
.found_divisor:
       mov byte[is_prime], 0
done:
       mov   eax,1       ; The system call for exit (sys_exit)
       xor   ebx,ebx     ; Exit with return code of 0 (no error)
       int   80h

因此,在循环中只有一个未采用的分支,然后使用采用的条件分支进行循环。它几乎不重要,因为div吞吐量是唯一的瓶颈,但一般情况下尽可能保持insn不在你的循环中。

答案 1 :(得分:1)

div bl操作执行以下操作:

  

DIV r / m8 ---无符号除以AX / r8,结果存储在AL←商,AH←剩余。

所以你的代码

keith: mov eax, 0
   mov al, [esi]
   mov dl, al
   mov bl, 2

使用byte ptr [esi]的前8位设置AX,保存这些低8位,然后将此值除以bl=2

loopy: div bl         ; ax / bl with quotient in al and remainder in ah
   and ax, 1111111100000000b
   cmp ax, 0
   je  there

之前的行有些尴尬,因为and ax, 1111111100000000b只设置AL=0 - AND屏蔽了AX的低8位。另一种选择是MOV AL,0

之后,它将AX与零进行比较,这有点过于复杂,因为AL刚刚设置为零。

(在您的情况下)最简单(也是最容易理解的方式)将是以下代码:

  mov esi, number
keith: 
  movzx eax, byte ptr [esi]   ; or byte [esi] if your using NASM
  mov dl, al                  ; saving this byte
  mov bl, 2
loopy: 
  div bl                      ; ax / bl with quotient in al and remainder in ah
  cmp ah, 0                   ; is remainder = 0 ?
  je  there                   ; then go to 'there'
...