我刚刚开始学习x86架构的汇编语言。我正在做一些基于条件分支的练习。目前,就代码分支而言,我已经介绍了 JMP , JZ 和 JNZ 指令。这是练习的link。第5点说如果输入为0
,则程序会搞砸。这是公平的,因为dec eax
会导致eax
注册保留0xffffffff
。我需要解决这个问题。这是我想出的 -
添加一个伪指令add eax, 0h
只是为了检查这是否导致zero-flag
被设置,如果设置跳转到代码中的某个位置,它会打印出所需的值并退出程序。
start:
; The program begins here:
call read_hex
add eax, 0h; my code
jz lb2; my code
mov ecx,1
lb1:
add ecx,ecx
dec eax
jnz lb1
mov eax,ecx
jmp lb3
lb2:
inc eax
call print_eax
; Exit the process:
push 0
call [ExitProcess]
lb3:
call print_eax
; Exit the process:
push 0
call [ExitProcess]
令我感到不舒服的是我在lb2
和lb3
中有重复的代码,我不确定是否有任何解决方法。
答案 0 :(得分:2)
start:
; The program begins here:
call read_hex
add eax, 0h; my code
jz lb2; my code
mov ecx,1
lb1:
add ecx,ecx
dec eax
jnz lb1
mov eax,ecx
jmp lb3
lb2:
inc eax
lb3:
call print_eax
; Exit the process:
push 0
call [ExitProcess]
答案 1 :(得分:1)
如果您想一次(使用add same,same
)循环将左移一位,简化对count = 0情况的处理,只需跳过移位循环到{{ 1}}指令,可让您删除mov
jmp lb3
是根据EAX是否为零来设置ZF的最佳方法。 (不是test eax,eax
)
add eax,0
有关处理在底部具有分支的循环的更多信息,请参见Why are loops always compiled into "do...while" style (tail jump)?,即使在循环可能需要运行0次的情况下。
在您的情况下,EAX = 0情况下对EAX的数据依赖没有好处。 (start:
; The program begins here:
call read_hex
mov ecx,1
test eax, eax
jz no_shift
shift_loop: ; like shl ecx, eax without wrapping the shift count
add ecx,ecx
dec eax
jnz shift_loop
no_shift:
mov eax,ecx
call print_eax
; Exit the process:
push 0
call [ExitProcess]
)。在这种情况下,这抵消了分支预测+投机执行的某些好处;使用inc eax
使EAX = 1意味着分支之后的代码可以在EAX的旧值还没有准备好供分支实际检查预测之前使用EAX。 (如果预测正确)。
您根本不需要为此进行任何分支。 x86的移位指令使创建mov eax, ecx
非常容易。
1 << n
这比使用可变计数start:
; The program begins here:
call read_hex
xor edx, edx ; edx = 0
bts edx, eax ; edx |= 1<<eax
mov eax, edx
call print_eax
push 0
call [ExitProcess]
(在Sandybridge系列上为3 oups)更有效,但是在包装移位计数方面具有完全相同的语义:
shl
如果您确实需要大的输入以将位完全移出并保持零,则可以为此分支,或者可以考虑使用SSE2或MMX整数移位,它们确实使移位计数饱和而不是将其包装。 (即mov ecx, eax
mov eax, 1
shl eax, cl
)