if-statementassemblygnu-assembler

If-Else in assembly doesn't seem to work or I'm doing smth wrong


.section .note.GNU-stack,"",@progbits

.section .data
cf_msg:
.string "CF = %d\n"
of_msg:
.string "OF = %d\n"
sf_msg:
.string "SF = %d\n"
less_msg:
.string "a less than b"
not_less_msg:
.string "a not less than b"
below_msg:
.string "a below b\n"
not_below_msg:
.string "a not below b\n"

.section .text
.globl main
.type main, @function
main:
pushq %rbp
movq %rsp, %rbp

# Pair 1: 53 and 25
movb $53, %dil
movb $25, %sil
call check_flags_and_compare

call clear_flags

# Pair 2: 53 and 91
movb $53, %dil
movb $91, %sil
call check_flags_and_compare

call clear_flags

# Pair 3: 53 and -43
movb $53, %dil
movb $-43, %sil
call check_flags_and_compare

call clear_flags

# Pair 4: -98 and -45
movb $-98, %dil
movb $-45, %sil
call check_flags_and_compare

movq $0, %rax
popq %rbp
ret


check_flags_and_compare:
pushq %rbp
movq %rsp, %rbp

# Arguments: %dil (a), %sil (b)
# Preserve arguments
movb %dil, %r8b
movb %sil, %r9b

# Perform addition to set flags
movb %r8b, %al
addb %r9b, %al

# Store flags
setc %r12b
seto %r13b
sets %r14b

# Print CF
movq $cf_msg, %rdi
movzbq %r12b, %rsi
call printf

# Print OF
movq $of_msg, %rdi
movzbq %r13b, %rsi
call printf

# Print SF
movq $sf_msg, %rdi
movzbq %r14b, %rsi
call printf

# Compare signed
cmpb %r9b, %r8b
jl signed_less
movq $not_less_msg, %rdi
call puts
jmp signed_done
signed_less:
movq $less_msg, %rdi
call puts
signed_done:
movb %dil, %r8b
movb %sil, %r9b

# Compare unsigned
cmpb %r9b, %r8b
jb unsigned_below
movq $not_below_msg, %rdi
call puts
jmp unsigned_done
unsigned_below:
movq $below_msg, %rdi
call puts
unsigned_done:

popq %rbp
ret

clear_flags:
xor %eax, %eax      # Clear SF
clc                 # Clear CF
movb $0, %al
add %al, %al        # Clear OF 
ret

This is the output i get(the flags should be fine):

CF = 0, OF = 0, SF = 0,

a less than b, a not below b

CF = 0, OF = 1, SF = 1, a less than b, a not below b,

CF = 1, OF = 0, SF = 0, a less than b, a not below b,

CF = 1, OF = 1, SF = 0, a less than b, a not below b,

This is what it should look like imo:

CF = 0, OF = 0, SF = 0,

a not less than b, a not below b,

CF = 0, OF = 1, SF = 1,

a less than b, a below b,

CF = 1, OF = 0, SF = 0,

a not less than b, a below b,

CF = 1 OF = 1 SF = 0

a less than b a below b


Solution

  • You haven’t specified the OS, but from the use of GAS and your calling convention I’m going to assume GNU/Linux.

    In the SYSV calling convention used for x86-64 Linux, r8 and r9 are caller-saved registers, that is, they will not be preserved across a function call. You are stashing the arguments to check_flags_and_compare in r8 and r9, then calling printf, which will likely overwrite these registers.

    Consider using different registers, or use stack variables.

    Note that in the SYSV calling convention, only rbx, rsp, rbp, r12, r13, r14, and r15 are callee-saved registers (preserved across a function call); all others are caller-saved. rsp is automatically saved by virtue of the fact that the stack must be balanced on entry and exit; the remaining registers must be saved and restored by any function that modifies them.

    As such, you should also be saving and restoring the values of r12-r14 in your function since you are modifying them, in order to respect the ABI; if you don’t save them, and your function gets called by a C function, strange and hard to debug errors may result.