I have only just started to learn the assembly language for the x86 architecture. I am doing some exercises based on conditional branching. At this point in time, I have been introduced to the JMP, JZ and JNZ instructions as far as branching of code is concerned. This is the link for the exercise. Point 5 says if input is given as 0
, the program screws up. That's fair, since dec eax
would result in the eax
register to hold 0xffffffff
. I need to fix this. Here is what I have come up with -
Add a dummy instruction add eax, 0h
just to check if this results in the zero-flag
being set, and if set jump to a place in the code, which prints out the required value and exit the program.
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]
The thing that's making me uncomfortable is that I have duplicate code in lb2
and lb3
and I am not quite sure if there is any way around this.
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]
If you wanted to keep your left-shift one bit at a time (with add same,same
) loop, simplify the handling of the count=0 case to just skipping over the shift loop to the mov
instruction, allowing you to remove the jmp lb3
test eax,eax
is the best way to set ZF according to whether EAX is zero or not. (Not add eax,0
)
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]
See Why are loops always compiled into "do...while" style (tail jump)? for more about handling loops with the branch at the bottom, even for cases where the loop might need to run 0 times.
In your case, there's no benefit to having a data dependency on EAX for the EAX=0 case. (inc eax
). This defeats some of the benefit of branch prediction + speculative execution for that case; using mov eax, ecx
to make EAX=1 means code after the branch can use EAX before the old value of EAX was even ready for the branch to actually check the prediction. (If it was correctly predicted.)
You don't need to branch at all for any of this. x86 has shift instructions that make it very easy to create 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]
This is even more efficient than using a variable count shl
(3 uops on Sandybridge-family), but with exactly equivalent semantics for wrapping the shift count:
mov ecx, eax
mov eax, 1
shl eax, cl
If you really need large inputs to shift the bit all the way out and leave zero, you could branch for that or you could consider using SSE2 or MMX integer shifts, which do saturate the shift count instead of wrapping it. (i.e. count & 0x3f
)
User contributions licensed under CC BY-SA 3.0