2017-10-21 78 views
1

我目前正在学习大学的程序集,最近开始编写程序集以点亮32x32 LED模拟器上的LED。为什么逻辑在组件中左移和/或某些值

本周我们有一个实验室,第一个问题是“创建一个程序,点亮随机的单个LED并继续,直到所有的LED点亮”。我有一个朋友告诉我他们是如何做到的,但我仍然对某些选项的工作方式感到困惑。 继承人的代码:

.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关闭,如果它已经打开了?

回答

1

CL是ecx的低字节。你将它与EFLAGS中的进位标志CF混淆。 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指令,只需要在一个寄存器中获得该行的旧值,在另一个寄存器中获得一个随机数,并且设置EAX '在EBX中有点位。


假设你的函数调用约定仅则会覆盖ECX和EDX(加上返回值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和或)可能是bts edi, eax

invoke是可能推动和清理call后栈中的宏,这样你就可以更有效利用mov商店堆栈,留下的空间存在。另外,如果您使用的CPU足够新,则可以使用rdrand ebx来获得乐趣。有趣的是:移位指令掩盖了计数,所以无论您使用什么输入,它们总是会移动0-31,所以RDRAND ECX之后的位位置不需要and ecx, 31

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

+0

感谢您的输入,但我仍然困惑,为什么'shl'和'或'操作正在执行,你能解释一下吗? –

+0

@JamieHyland:更新了答案。单步执行代码并观察寄存器中的值变化应该有助于理解'row | =(1 << rand)'是否不能解释它。 –

+1

@JamieHyland:我想看看循环是多么复杂,所以我重写了函数。您只需要2个保存呼叫的寄存器和两个暂存寄存器,不需要静态存储。 –

相关问题