//----------------------------------------------------------------- // 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: 08 FEB 2008 -- eliminated 'and', 'or', 'xor' //----------------------------------------------------------------- .equ sys_exit, 1 # system-call ID-number .equ sys_write, 4 # system-call ID-number .equ STDOUT, 1 # device-file ID-number .section .data ten: .int 10 # the decimal-system radix total: .int 0 # for sum of the arguments msg: .ascii "The total is " # title for program output buf: .ascii " \n" # buffer for result-string len: .int . - msg # length of program-output .section .text _start: mov $0, %ebx # EBX=initial array-index nxarg: mov 8(%esp, %ebx, 4), %esi # load address of next arg cmp $0, %esi # NULL-pointer encountered? je argxx # yes, end-of-list reached call atoi # else convert arg to int add %eax, total # then add int to total inc %ebx # advance our array-index jmp nxarg # and process another arg argxx: mov total, %eax # load EAX with final total mov $buf, %edi # point EDI to output buffer call itoa # convert integer to string mov $sys_write, %eax # system-call ID-number mov $STDOUT, %ebx # device-file ID-number mov $msg, %ecx # address of message mov len, %edx # length of message int $0x80 # invoke kernel service mov $sys_exit, %eax # system-call ID-number mov $0, %ebx # use zero as exit-code int $0x80 # invoke kernel service itoa: # This procedure converts the unsigned integer found in EAX into # its representation as a decimal digit-string at address (EDI). pushal # preserve CPU registers mov $0, %ecx # initialize digit-count nxdiv: mov $0, %edx # extend dividend to 64-bits divl ten # divide EDX:EAX by ten push %edx # push remainder onto stack inc %ecx # and count this remainder cmp $0, %eax # was the quotient nonzero? jne nxdiv # yes, generate another digit nxdgt: pop %edx # pop next remainder to EDX add $'0', %dl # convert number to numeral mov %dl, (%edi) # store numeral into buffer inc %edi # and advance buffer-index loop nxdgt # again if digits remain popal # restore saved registers ret # return to the caller atoi: # This function converts a string of decimal numerals found at the # address in ESI into an unsigned integer which it returns in EAX. push %ebx # preserve working registers push %edx push %esi mov $0, %eax # initialize the accumulator nxchr: mov $0, %ebx # clear all the bits in EBX mov (%esi), %bl # load next character in BL inc %esi # 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 mull ten # multiply accumulator by ten add %ebx, %eax # and then add the new integer jmp nxchr # go back for another numeral inval: pop %esi # recover saved registers pop %edx pop %ebx ret # and return to the caller .global _start # make entry-point visible .end # nothing more to assemble