//---------------------------------------------------------------- // squares.s // // This assembly language program displays a numerical table // showing the squares of the first twenty positive integers // with 'add' and 'inc' as its only arithmetical operations. // // A subroutine is used to convert each numerical value to a // decimal digit-string which is "right-justified", in order // to obtain a table whose columns are attractively aligned. // // to assemble: $ as squares.s -o squares.o // and to link: $ ld squares.o -o squares // and execute: $ ./squares // // programmer: ALLAN CRUSE // written on: 22 FEB 2009 //---------------------------------------------------------------- # manifest constants .equ MAXARG, 20 # total number of lines .equ sys_WRITE, 1 # system-call ID-number .equ sys_EXIT, 60 # system-call ID-number .equ dev_STDOUT, 1 # device-file ID-number .section .data head: .ascii "\n Table of Squares\n\n" hlen: .quad . - head body: .ascii " \n" blen: .quad . - body foot: .ascii "\n" # newline format-string flen: .quad . - foot .section .bss arg: .space 8 # to hold current number val: .space 8 # to hold current square .section .text _start: # print the table's title mov $sys_WRITE, %rax # system-call ID-number mov $dev_STDOUT, %rdi # device-file ID-number mov $head, %rsi # address of the buffer mov hlen, %rdx # length of the message syscall # invoke kernel service # program loop, to print table's body movq $0, val # initial value for square movq $0, arg # initial value for number again: # compute the next square mov arg, %rax # fetch the current number add %rax, %rax # compute twice its value inc %rax # add one to that result add %rax, val # and add to prior square # and compute next number incq arg # increment current number # format the next line of the table mov arg, %rax # value for field #1 mov $3, %rcx # width for this field lea body+7, %rdi # right-hand position call rjitoa # convert num to ascii mov val, %rax # value for field #2 mov $3, %rcx # width for this field lea body+17, %rdi # right-hand position call rjitoa # convert num to ascii # print the next line of the table mov $sys_WRITE, %rax # system-call ID-number mov $dev_STDOUT, %rdi # device-file ID-number lea body, %rsi # address of the buffer mov blen, %rdx # length of the message syscall # invoke kernel service # check the loop's exit-condition cmpq $MAXARG, arg # does arg equal maximum? je finis # yes, jump ahead to finish jmp again # else display another line finis: # print a blank bottom line, and return to the shell mov $sys_WRITE, %rax # system-call ID-number mov $dev_STDOUT, %rdi # device-file ID-number lea foot, %rsi # address of the buffer mov flen, %rdx # length of the message syscall # invoke kernel service # terminate this application mov $sys_EXIT, %rax # system-call ID-number mov $0, %rdi # use zero as exit-code syscall # invoke kernel service .global _start # make entry-point visible # # This procedure converts the unsigned integer value found in RAX # to its representation as a (right-justified) string of numerals # whose width is found in RCX and whose position is found in RDI. # # EXPECTS: RAX = integer to be converted # RDI = address of output field # RCX = length for output field # rjitoa: push %rax # save caller's registers push %rbx push %rcx push %rdx push %rdi mov $10, %rbx # base of the number system add %rcx, %rdi # right-hand edge of field nxdiv: xor %rdx, %rdx # next dividend in (RDX,RAX) div %rbx # perform division by radix add $'0', %dl # convert remainder to ascii dec %rdi # move the pointer leftward mov %dl, (%rdi) # and store the numeral there or %rax, %rax # was quotient equal to zero? loopnz nxdiv # no, generate another digit jrcxz nopad # then stop if field is full mov $' ', %dl # else setup space character nxpad: dec %rdi # move the pointer leftward mov %dl, (%rdi) # and store the space there loop nxpad # again until field is full nopad: pop %rdi # restore the saved registers pop %rdx pop %rcx pop %rbx pop %rax ret # return control to caller .end # nothing else to assemble