user multiplied by 5. We want it to be the third parameter so we move it to r2. Then we load the value of the number entered by the user into r1. Finally we load inr0 the address to the format message of printf. Note that here the order of preparing the arguments of a call is not relevant as long as the values are correct at the point of the call. We use the fact that we will have to overwrite r0, so for convenience we first copy
r0 tor2.
$ ./printf02
Hey, type a number: 1234<CR> 1234 times 5 is 6170
9.5
Unified Assembler Language
As we write functions in the future, we will find that an efficient programming technique is to divide up the processes we wish to encode into individual functions that perform, if possible, just one of the needed operations. We can then test each part of the program separately and when we are done have many useful, already tested, functions to use in other programs. This technique is called Functional Programming.
One problem arises when we combine many well-tested functions into one larger pro- gram: repeated use of the same label. Looking ahead we see programs with loops and often the label loop:. Other common labels are exit:, error:, and next:. If we use the same label in different functions, we will get an assembler error.
A modern syntax called Unified Assembler Language (UAL) allows for numerical labels that may be repeated throughout a program.
The syntax is to add the directive.syntax unified as in the following trivial example:
/* -- numericalLabels.s */
.global main /* entry point must be global */
.syntax unified /* modern syntax (UAL=Unified Assembler Language) */ .text
main: /* This is main */
push {r4, lr} ldr r0, =message1 bl puts
b 1f /* Goto the first label 1 forward */ b 2f /* Goto the first label 2 forward */ 1:
ldr r0, =message2 bl puts
1:
9. Functions
pop {r4, pc} /* Return from main */
2:
ldr r0, =errmessage bl puts
b 1b /* Goto the first label 1 backward */
.data
message1: .asciz "Numerical Label Test\n" message2: .asciz "Success\n"
errmessage: .asciz "Failure!\n"
.global puts
Projects
1. Simplify the rest of the comments in the above example.
2. Write some other simple functions and test them. In particular, include some functions that operate on strings (Section 8.6).
3. Interchange lines 5 and 8 in hello01.s and show that there is no need for extra alignment. Does this give a general rule for more efficient coding? Can you think of any disadvantages of doing that?
4. Look up details of the prototypes for such functions asprintfandscanf. Justify what is placed in the registers r0 and r1.
5. There are many other C functions we may use in the same way as printf and
scanf. Look up their prototypes and write simple programs using them.
6. The.data sections in bothprintf01.s and printf02.s have many unnecessary
.align statements. Rewrite them without any such statements. [Hint: .word
before .asciz.]
10 Searching and Sorting
We now have enough background to program some more complicated functions in as- sembler. One very important need is to be able to search through a file of information to find a particular item. In general, we look for a key which is a unique identifier such as a social security number. If a file has N elements in it, on average it will take N/2 accesses to find the key if the file is randomly arranged. We will first consider the
Binary Search that finds a key in a sorted list in about log2(N) accesses which is a great improvement for large files. Having seen that improvement, we will then consider methods of sorting files so that the Binary Search can be performed.
10.1
Binary Search
Having the list sorted allows us to use the Binary Search method to find the key. At each stage we cut in half the number of places at which the key could appear. Since that can only happen about log2N times, even in the case where the key does not appear, this method is far superior to a sequential search (particularly for large values of N). Should the list contain 220 = 1,048,576 elements, at worst 21 accesses would be necessary rather than an average of 524,288 when it appears in the unsorted list and 1,048,576 when it does not appear in the list.
Our code follows directly from some C/C++/Java code such as
int binary_search(int[] array, int size, int key) { int low = 0, high = size - 1;
while( low <= high)
{int mid = (low + high) / 2; if( array[ mid ] < key )
low = mid + 1;
elseif( key < array[ mid ] ) high = mid -1; else return mid; } return NOT_FOUND; }
10. Searching and Sorting
Since the input of the values is not significant in this example, we will “hard-wire” into our code the list of integers using the .word directive. The line
array: .word 2,5,11,23,47,95,191,383,767,998
both reserves 10 words in memory labeled by the name “array”, but also initializes those words to the given values.
1 @ BinarySearch.s 2 @
3 @ Demonstrates binary search on a fixed list of integers 4 .data @ Data declaration section
5 return: .word 0
6 array: .word 2,5,11,23,47,95,191,383,767,998 7 num_read: .word 0
8 prompt: .asciz "\nInsert integer key (key < 0 to quit): " 9 scanFMT: .asciz "%d" @ Format pattern for scanf
10 echo: .asciz "\nYou entered: %d\n"
11 ymsg: .asciz "\nKey was found at position %d\n"
12 nmsg: .asciz "\nKey not found! a near index is: %d\n" 13
14 .text @ Start of code section 15 .global main
16 main:
17 ldr r1, =return @ r1 <- &return
18 str lr, [r1] @ *r1 <- lr save return address 19
20 input:
21 ldr r0, =prompt @ r0 <- &prompt
22 bl puts @ Print prompt
23
24 ldr r0, =scanFMT @ r0 <- &scanFMT 25 ldr r1, =num_read @ r1 <- &num_read
26 bl scanf @ Call to scanf; puts value in num_read 27 @echo
28 ldr r0, =echo
29 ldr r1, =num_read
30 ldr r1, [r1]
31 bl printf @ echo the key
32
33 @check sentinal
34 ldr r1, =num_read @ r1 <- &num_read 35 ldr r1, [r1] @ r1 <- *r1
36 cmp r1, #0 @ Look for sentinal (negative) 37 blt exit @ quit if num_read is negative
10.1. Binary Search
38
39 mov r6, r1 @ Put key in r6
40 ldr r7, =array @ Address of array in r7 41
42 mov r0, #0 @ r0 = low = 0 (index) 43 mov r1, #9 @ r1 = high = 10 - 1 44
45 Loop:
46 cmp r1, r0 @ test high - low
47 blt fail @ while( low <= high ) 48 @get middle
49 add r3, r0, r1 @ r3 <- low + high 50 mov r3, r3, ASR #1 @ r3 <- r3 / 2 = mid 51 mov r8, r3 @ save index for printing 52 add r5, r7, r3, LSL #2 @ r5 <- &array[4*mid] 53 ldr r5, [r5] @ r5 <- array[4*mid] 54 cmp r5, r6 @ test array[4*mid] - key 55 blt RH @ if (array[4*mid] < key) 56 bgt LH @ if (array[4*mid] > key)
57 b found
58 RH: add r0, r3, #1 @ low = mid + 1 (index)
59 b Loop
60 LH: sub r1, r3, #1 @ high = mid - 1 (index)
61 b Loop
62 @ found 63 found:
64 add r1, r8, #1 @ get index in normal count
65 ldr r0, =ymsg
66 bl printf @ Print yes message
67
68 b input @ begin again
69
70 @ not found 71 fail:
72 add r1, r8 , #1 @ get index in normal count
73 ldr r0, =nmsg
74 bl printf @ Print not found message 75
76 b input @ try again!
77 @ exit 78 exit:
79 ldr r1, =return @ r1 <- &return
80 ldr lr, [r1] @ lr <- *r1 saved return address
10. Searching and Sorting 82 83 /* External */ 84 .global puts 85 .global printf 86 .global scanf
As often happens in moving from C/C++/Java to assembler, we must take special pains to distinguish between indexes in the high level language and memory addresses (4 bytes to each word). In particular, in line 52 we shift left two places in order to multiply by four thus counting in words instead of bytes. In addition, we have to watch for alignment problems.