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