我目前正在大学学习装配,并且最近开始编写装配程序,以便在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已经打开的情况下意外关闭它?
答案 0 :(得分:1)
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)
来设置一个随机位(可能已经设置好了)。
如果您想了解此代码的运作方式,请在调试器中单步调试并观察寄存器中的值更改。另请参阅x86标记wiki以获取指南,文档和调试技巧。
BTW,创建一个1位掩码的更有效方法是xor ebx,ebx
/ bts ebx, eax
,以避免在ECX中需要移位计数,但如果你还没有学过BTS,它没有做任何你不能用其他简单指令做的事情。
实际上,BTS意味着您根本不需要单独的掩码和OR指令,只需在一个寄存器中获取该行的旧值,在另一个寄存器中获取一个随机数,然后bts ebx, eax
在EBX中设置EAX位。
假设您的函数调用约定只包含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和or)可以是bts edi, eax
。
invoke
是一个宏,可能会在call
之后推送并清理堆栈,因此通过使用mov
存储到堆栈并将空间留在那里可以提高效率。此外,如果您使用足够新的CPU,则可以使用rdrand ebx
来获得乐趣。
有趣的事实:移位指令会掩盖计数,因此无论您使用什么输入,它们总是移位0-31,因此在and ecx, 31
之后您不需要RDRAND ECX
位位置。< / p>
此外,您可以调用random 32*32
并将结果拆分为行位和列位。