为什么逻辑左移&或汇编中的某些值

Why logic shift left & or certain values in assembly

我目前正在大学学习汇编,最近开始编写汇编程序来点亮 32x32 LED 模拟器上的 LED。

我们这周有一个实验室,第一个问题是 "create a program that lights up a random individual LED and continues until all LED's are lit"。我有一个朋友向我展示了他们是如何做到的,但我仍然对某些选项的工作原理感到困惑。 代码如下:

.data
x           DWORD   0
y           DWORD   0
row         DWORD   0
row_copy    DWORD   00000001h

.code
    main:nop

        invoke version
        invoke setPattern, 0
    row_random:
        invoke random, 32 ;create a random number between 0-31
        mov x, eax        ;move that value into memory location x
        invoke readRow, x ;select a row to be altered 
        mov row, eax 
    row_on:
        invoke random, 32
        mov ecx, eax      ;move the random value into ecx
        shl row_copy, CL  ;shift left with carry flag (This is where Im confused)
        mov eax, row
        mov ebx, row_copy
        or eax, ebx ; I'm also unsure as to why this is happening 
        invoke writeRow, x, eax  ;alter a pixel at the random row x with the value of eax
        mov row_copy, 00000001h
        ;invoke Sleep, 1
        jmp row_random
        invoke ExitProcess,0

最初,当我这样做时,我创建了一个介于 0-31 之间的随机数,将其设置在 EBX 中并将 writeRow 与 x 和 ebx 一起使用。然而那是错误的。有人可以向我解释为什么您在逻辑上向左移动 CL 吗?以及为什么它需要这两个值?我认为 或 是为了确保您不会不小心将已经打开的 LED 关闭?

CL是ecx的低字节。您将它与 CF(EFLAGS 中的进位标志)混淆了。 x86 variable-count shift instructions require the shift-count to be in cl.

郑重声明,该代码 可笑 效率低下。 row_copy 用内存目标指令移位(慢),然后加载,然后再次替换为 1!所以...你可以做到

mov    ecx, eax
mov    ebx, 1
shl    ebx, cl

像个正常人。根本没有理由为 row_copy 设置内存位置,只需在寄存器中进行即可。当您 运行 超出寄存器时,您只需要静态内存存储。


代码实现的基本逻辑是row |= (1 << rand_0_31)设置一个随机位(可能已经设置)。

如果您想查看此代码的运行方式,请在调试器中单步执行它并观察寄存器中值的变化。另请参阅 标签 wiki 了解指南、文档和调试提示。


顺便说一句,一种更有效的创建 1 位设置掩码的方法是 xor ebx,ebx / bts ebx, eax 以避免在 ECX 中需要移位计数,但是如果你还没有了解 BTS然而,它不会执行其他更简单的指令无法执行的任何操作。

实际上,BTS 意味着您根本不需要单独的掩码和 OR 指令,只需在一个寄存器中获取行的旧值,在另一个寄存器中获取随机数,然后 bts ebx, eax 设置 EBX 中的第 EAX 位。


假设您的函数调用约定仅破坏 ECX 和 EDX(加上具有 return 值的 EAX),您不需要任何静态存储位置,只需寄存器。我会做类似的事情:

; untested
.code
    main:
        push  ebx   ; save a couple call-preserved registers
        push  edi   ; for values that survive across function calls

        ; nop       ; what's the point of this NOP?
        invoke version
        invoke setPattern, 0

    row_random:
        invoke random, 32 ;create a random number between 0-31
        mov    ebx, eax         ; eax = ebx = row
        invoke readRow, eax
        mov    edi, eax         ; edi = old value of row
        invoke random, 32

        mov    ecx, eax         ; ecx = random column = bit position
        mov    eax, 1
        shl    eax, cl          ; 1 << random
        or     edi, eax         ; row_value |= 1<<random

        invoke writeRow, ebx, edi  ; pixel[ebx] |= 1<<random

        jmp row_random
        ; or loop a finite number of times with dec / jnz.

        pop  edi
        pop  ebx
        return
        ;  invoke ExitProcess,0

整个中间块(带有 shl 和 or)可以是 bts edi, eax

invoke 是一个宏,它可能会在 call 之后压入并清理堆栈,因此通过使用 mov 存储到堆栈并将space 那里。此外,如果您使用的是足够新的 CPU,您可以使用 rdrand ebx 来获得乐趣。

有趣的事实:移位指令屏蔽了计数,因此无论您使用什么输入,它们总是移位 0-31,因此您不需要在 RDRAND ECX 之后的 and ecx, 31 作为位位置.

此外,您可以调用 random 32*32 并将结果拆分为行位和列位。