In my last post, I reversed a string. I want to build on that to make reusable code; I want to reverse multiple strings. I do this by turning the reverse code into a function and then call it.
The first step is to reorder the code so that the logic to reverse the string is at the end, and is called using the BL (Branch with link) instruction. We also need to add a return at the end of our code so that we can continue processing. We make sure that the code to exit the program sits in between the calling point and the function. The body of the code now looks like this:
mov x0, #1 /* fd := STDOUT_FILENO */ ldr x1, =msg /* buf := msg */ ldr x2, =len /* count = lent */ bl reverse /* syscall exit (int status) */ mov x0, #0 /* status := 0 */ mov w8, #93 /* exit is syscall #1 */ svc #0 reverse: add X4, X1, 0 add X6, x1, x2 sub X6, X6, #1 loop: sub X6, X6, #1 ldrb w5, [x4] ldrb w7, [x6] strb w5, [X6] strb w7, [X4] add X4, X4, #1 cmp X4, X6 b.lt loop mov w8, #64 svc #0 ret |
Running this code looks like this:
$ make reversed ; ./reversed as -g -o reversed.o reversed.s ld -o reversed reversed.o CBA |
Now I can add a couple other strings and call them, too.
And…I just learned something. The first time I added the strings, I added them like this:
msg1: .ascii "ABC\n" msg2: .ascii "Hello, World!\n" msg3: .ascii "1234567890\n" len1 = . - msg1 len2 = . - msg2 len3 = . - msg3 |
This is obviously wrong, and I understand why. Now. What was interesting was how it manifested itself. This was the output:
$ make reversed ; ./reversed as -g -o reversed.o reversed.s ld -o reversed reversed.o 0987654321 !dlroW ,olleH CBA |
Yes! It worked! Um, wait…I didn’t actuially call the function multiple times? What is going on here?
The error, of course, is due to the calculation of the length of the string. The macro len1 = . – msg1 is based on their locations in code. This calculated the length of the entire string buffer, multiple lines, including the \n characters, and reversed the entire buffer in one pass.
How are we going to write a test for this case into our code? Call them in different orders. Instead of calling reverse for msg1, then 2, then 3, I’ll call 2 first, then 1, then 3. Like this:
mov x0, #1 /* fd := STDOUT_FILENO */ ldr x1, =msg2 /* buf := msg */ ldr x2, =len2 /* count = lent */ bl reverse mov x0, #1 /* fd := STDOUT_FILENO */ ldr x1, =msg1 /* buf := msg */ ldr x2, =len1 /* count = lent */ bl reverse mov x0, #1 /* fd := STDOUT_FILENO */ ldr x1, =msg3 /* buf := msg */ ldr x2, =len3 /* count = lent */ bl reverse |
$ make reversed ; ./reversed as -g -o reversed.o reversed.s ld -o reversed reversed.o !dlroW ,olleH CBA 0987654321 |