### Linux assembly language program to demonstrate multiple return ### values without using the C standard library. ### It occurred to me that multiple return values are a useful ### assembly-language idiom that isn’t supported well in C, which ### tends to push C programmers to overload single return values to ### indicate errors, with tragic results. It occurred to me that the ### C standard library function `atoi` is a good example of this ### problem; it provides no way to detect errors, forcing you to ### choose between using the awkward `strtol` and ignoring invalid ### input. Here’s an example of a variant `atoi` that returns the ### converted integer in `rax` and the pointer past the end of the ### integer in `rdi` rather than requiring a separate pointer ### parameter the way `strtol` does. Because it’s just an example, it ### doesn’t bother to handle leading whitespace or signs, though those ### would be easy to add. ### I’m mostly using the standard Linux SysV amd64 calling convention ### here, except that I’m not observing the 16-byte stack alignment ### constraint on calls, and `putsi` and `print_num` take their ### arguments in weird registers. ### See also twoatoi_struct.c. .intel_syntax noprefix .globl main main: push rbx push rbp mov rbx, rdi # argc mov rbp, rsi # argv jmp 3f # skip first arg (program name) ## Loop over command-line arguments, printing each. 1: mov rdi, [rbp] # Load command-line argument. call atoi_two # Convert to binary. cmp rdi, [rbp] # Success if pointer changed. jne 2f call print_fail jmp 3f 2: call print_num # Implicitly pass `rax` from atoi_two mov dil, ' ' call putc 3: add rbp, 8 # Move to next argument. dec rbx # (`dec` sets zero flag ZF, just not CF) jnz 1b # Loop if any arguments left. mov dil, '\n' call putc pop rbp pop rbx xor eax, eax ret print_fail: lea rsi, [rip + 1f] mov rdx, offset fail_len call putsi ret 1: .ascii "(fail) " fail_len = . - 1b ## Print a byte. putc: push rdi mov rsi, rsp mov rdx, 1 call putsi pop rax ret ## `print_num` prints the number in `rax` (not `rdi`). 1: .quad 10 # divisor print_num: xor edx, edx # Zero the high-order bits of the dividend. div qword ptr [rip + 1b] # Divide `rax` by 10. test rax, rax # is quotient 0? jz 2f # If not, push rdx # Save remainder for recursion, and call print_num # do recursive call with quotient. pop rdx 2: lea rdi, [rdx + '0'] # Convert remainder to decimal. call putc ret ## Invoke write(2) for stdout with args in rsi (string) and ## rdx (string length). putsi: mov eax, 1 # SYS_write mov edi, 1 # stdout fd syscall ret atoi_two: xor eax, eax # Initialize return value to 0 jmp 1f 2: imul rax, rax, 10 add rax, rdx inc rdi 1: movzx edx, byte ptr [rdi] sub dl, '0' cmp dl, 9 jbe 2b ret # Implicitly return continuation pointer in rdi.