//----------------------------------------------------------------- // add.s // // This program computes the sum of all nonnegative integers // which a user supplies as command-line arguments, and then // writes the resulting total on the console display screen. // Its source-code provides assembly language algorithms for // performing data-conversions between integers and strings. // // assemble with: $ as add.s -o add.o // and link with: $ ld add.o -o add // execute using: $ ./add ... // // programmer: ALLAN CRUSE // written on: 06 FEB 2008 // revised on: 11 FEB 2009 -- for x86_64 Linux environment //----------------------------------------------------------------- .equ sys_exit, 60 # system-call ID-number .equ sys_write, 1 # system-call ID-number .equ STDOUT, 1 # device-file ID-number .section .data ten: .quad 10 # the decimal-system radix total: .quad 0 # for sum of the arguments msg: .ascii "The total is " # title for program output buf: .ascii " \n" # buffer for result-string len: .quad . - msg # length of program-output .section .text _start: mov $0, %rbx # RBX=initial array-index nxarg: mov 16(%rsp, %rbx, 8), %rsi # load address of next arg cmp $0, %rsi # NULL-pointer encountered? je argxx # yes, end-of-list reached call atoi # else convert arg to int add %rax, total # then add int to total inc %rbx # advance our array-index jmp nxarg # and process another arg argxx: mov total, %rax # load RAX with final total mov $buf, %rdi # point RDI to output buffer call itoa # convert integer to string mov $sys_write, %rax # system-call ID-number mov $STDOUT, %rdi # device-file ID-number mov $msg, %rsi # address of message mov len, %rdx # length of message syscall # invoke kernel service mov $sys_exit, %rax # system-call ID-number mov $0, %rdi # use zero as exit-code syscall # invoke kernel service itoa: # This procedure converts the unsigned integer found in RAX into # its representation as a decimal digit-string at address (RDI). push %rax # preserve CPU registers push %rbx push %rcx push %rdx mov $0, %rcx # initialize digit-count nxdiv: mov $0, %rdx # extend dividend to 128-bits divq ten # divide RDX:RAX by ten push %rdx # push remainder onto stack inc %rcx # and count this remainder cmp $0, %rax # was the quotient nonzero? jne nxdiv # yes, generate another digit nxdgt: pop %rdx # pop next remainder to RDX add $'0', %dl # convert number to numeral mov %dl, (%rdi) # store numeral into buffer inc %rdi # and advance buffer-index loop nxdgt # again if digits remain pop %rdx # restore saved registers pop %rcx pop %rbx pop %rax ret # return to the caller atoi: # This function converts a string of decimal numerals found at the # address in RSI into an unsigned integer which it returns in RAX. push %rbx # preserve working registers push %rdx push %rsi mov $0, %rax # initialize the accumulator nxchr: mov $0, %rbx # clear all the bits in RBX mov (%rsi), %bl # load next character in BL inc %rsi # and advance source index cmp $'0', %bl # does character preceed '0'? jb inval # yes, it's not a numeral cmp $'9', %bl # does character follow '9'? ja inval # yes, it's not a numeral sub $'0', %bl # else convert numeral to int mulq ten # multiply accumulator by ten add %rbx, %rax # and then add the new integer jmp nxchr # go back for another numeral inval: pop %rsi # recover saved registers pop %rdx pop %rbx ret # and return to the caller .global _start # make entry-point visible .end # nothing more to assemble