Armstrong (or Narcissistic) numbers are a special set of integers.
An n-digit number is an Armstrong number if the sum of the n-th power of the digits is equal to the original number. For example, 371 is an Armstrong number because the sum of 3 cubed, 7 cubed and 1 cubed is equal to 371.
Here we compute all the Armstrong numbers of 2, 3 and 4 digits.
- Power function
# this subroutine returns the passed digit to the passed power # # inputs # r0 - digit # r1 - power # # outputs # r0 - digit ** power # # locals # r4 .globl _power .align 2 .text _power: nop stmfd sp!, {r4, lr} @ save variables to stack subs r1, r1, #1 @ leave unless power > 1 ble _power_end mov r4, r0 @ copy digit _power_loop_start: mul r0, r4, r0 @ raise to next power subs r1, r1, #1 beq _power_end @ leave when done b _power_loop_start @ next iteration _power_end: ldmfd sp!, {r4, pc} @ restore state from stack and leave subroutime
# inputs # r0 - number # # outputs # r0 - armstrong number # # local r4, r5, r6, r7, r8 .equ ten,10 .equ hundred,100 .equ thousand,1000 .equ ten_thousand,10000 number .req r4 width .req r5 digit .req r6 current .req r7 armstrong .req r8 .globl _armstrong .align 2 .text _armstrong: nop stmfd sp!, {r4, r5, r6, r7, r8, lr} @ save variables to stack mov number, r0 @ copy passed parameter to working number cmp number, #ten @ exit unless number > 10 blt _end ldr current, =ten_thousand @ exit unless number < 10000 cmp number, current bge _end mov width, #0 @ initialise mov digit, #0 mov armstrong, #0 ldr current, =thousand @ handle 1000 digit _thousand_start: cmp number, current blt _thousand_end @ exit thousand code if none left mov width, #4 @ width must be 4 add current, current, #thousand @ bump thousand counter add digit, digit, #1 @ and corresponding digit count b _thousand_start @ and loop _thousand_end: add number, number, #thousand @ need number modulo thousand sub number, number, current mov r0, digit @ push digit mov r1, width @ and width bl _power @ to compute digit **width add armstrong, r0, armstrong @ and update armstrong number with this value ldr current, =hundred @ then we do the hundreds as we did the thousands mov digit, #0 _hundred_start: cmp number, current blt _hundred_end teq width, #0 @ and only set width if it is currently unset moveq width, #3 _hundred_width: add current, current, #hundred @ yada yada as thousands above add digit, digit, #1 b _hundred_start _hundred_end: add number, number, #hundred sub number, number, current mov r0, digit mov r1, width bl _power add armstrong, r0, armstrong ldr current, =ten @ then the tens as the hundred and thousands above mov digit, #0 _ten_start: cmp number, current blt _ten_end teq width, #0 moveq width, #2 _ten_width: add current, current, #ten add digit, digit, #1 b _ten_start _ten_end: add number, number, #ten sub number, number, current mov r0, digit mov r1, width bl _power add armstrong, r0, armstrong mov r0, number @ then add in the trailing digits mov r1, width bl _power add armstrong, r0, armstrong mov r0, armstrong @ and copy the armstrong number back to r0 for return _end: ldmfd sp!, {r4, r5, r6, r7, r8, pc} @ restore state from stack and leave subroutine
We improve the legibility of the code by e.g. number .req r4. This textual substitution does increase the semantic value of the code considerably.
N.B. This is functionally equivalent but much shorter than the previous function. The variable \@ here is a magic variable, incremented each time the macro is instantiated. This enables the use of distinct labels, which we need here.
# inputs # r0 - number # # outputs # r0 - armstrong number # # local r4, r5, r6, r7, r8 .equ ten,10 .equ hundred,100 .equ thousand,1000 .equ ten_thousand,10000 number .req r4 width .req r5 digit .req r6 current .req r7 armstrong .req r8 .macro armstrong_digit a, b ldr current, =\a mov digit, #0 _start\@: cmp number, current blt _end\@ teq width, #0 @ and only set width if it is currently unset moveq width, #\b add current, current, #\a add digit, digit, #1 b _start\@ _end\@: add number, number, #\a sub number, number, current mov r0, digit mov r1, width bl _power add armstrong, r0, armstrong .endm .globl _armstrong .align 2 .text _armstrong: nop stmfd sp!, {r4, r5, r6, r7, r8, lr} @ save variables to stack mov number, r0 @ copy passed parameter to working number cmp number, #ten @ exit unless number > 10 blt _end ldr current, =ten_thousand @ exit unless number < 10000 cmp number, current bge _end mov width, #0 @ initialise mov armstrong, #0 armstrong_digit thousand 4 armstrong_digit hundred 3 armstrong_digit ten 2 mov r0, number @ then add in the trailing digits mov r1, width bl _power add armstrong, r0, armstrong mov r0, armstrong @ and copy the armstrong number back to r0 for return _end: ldmfd sp!, {r4, r5, r6, r7, r8, pc} @ restore state from stack and leave subroutine
Here we introduce the use of the macro assembly functions of as Again this change leads to improved code - we eliminate duplication thus improving maintainability and legibility.
.equ ten,10 .equ ten_thousand,10000 .section .rodata .align 2 string: .asciz "armstrong number of %d is %d\n" .text .align 2 .global main .type main, %function main: ldr r5, =ten ldr r6, =ten_thousand mov r4, r5 @ start with n = 10 _main_loop: cmp r4, r6 @ leave if n = 10_000 beq _main_end mov r0, r4 @ call the _armstrong function bl _armstrong teq r0, r4 @ if the armstong value = n print it bne _main_next @ else skip mov r2, r0 mov r1, r4 ldr r0, =string @ store address of start of string to r0 bl printf @ call the c function to display information _main_next: add r4, r4, #1 b _main_loop _main_end: mov r7, #1 @ set r7 to 1 - the syscall for exit swi 0 @ then invoke the syscall from linux
bob@poland:~/src/problems_for_computer_solution/07_armstrong_numbers$ ./armstrong4macro armstrong number of 153 is 153 armstrong number of 370 is 370 armstrong number of 371 is 371 armstrong number of 407 is 407 armstrong number of 1634 is 1634 armstrong number of 8208 is 8208 armstrong number of 9474 is 9474 bob@poland:~/src/problems_for_computer_solution/07_armstrong_numbers$
No comments:
Post a Comment