| 
    
      Example 9
     | 
    
       
     | 
It is imperative that you read this document alongside this
example... Thank you.
And this example is not 32-bit friendly - refer to the APCS
specification or recompile without preserving flags and see if it works.
In C, the traditional way is to create a routine along the lines of:
void lowercase(char string[])
{
   int  i = 0;
   while ( string[i] )
   {
      string[i] = tolower(string[i]);
      i++;
   }
   return;
}
That isn't bad, but if you do a lot of work with strings that need converting to lower case,
you can obtain benefits by converting a repetitive task, such as the lower case conversion,
to assembler.
As far as I'm aware, only three dialects of RISC OS exist. English, German, and Welsh. That
aside, there is no exuse these days for not making your lowercase routine deal with foreign
languages. It is simple. Very simple.
Like this:
; R0 = Pointer to string (set on entry)
; R1 = Byte read from string
; R2 = Pointer to lowercase table
lowercase
        STMFD    sp!, {v6, lr}
        MOV      R1, R0            ; Preserve string pointer
        SWI      &43040            ; "Territory_Number"
        SWI      &43057            ; "Territory_LowerCaseTable"
        MOV      R2, R0            ; Set lowercase table pointer
        MOV      R0, R1            ; Restore string pointer
lowercase_loop
        LDRB     R1, [R0]          ; Load character from R0
        CMP      R1, #0            ; Is it a null byte?
        LDMEQEA  sp!, {v6, pc}^    ; Return if null (end of string)
        LDRB     R1, [R2, R1]      ; Convert to indexed lowercase character
        STRB     R1, [R0], #1      ; Store character, increment offset pointer
        B        lowercase_loop    ; Mulberry bushes
#include <stdio.h>
#include <ctype.h>
#include "kernel.h"
#include "swis.h"
extern void lowercase(char *);
void o_lowercase(char []);
int main(void)
{
   char s[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+={[}]|\\:;\"\'<,>.?/ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+={[}]|\\:;\"\'<,>.?/ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+={[}]|\\:;\"\'<,>.?/ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+¤ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+={[}]|\\:;\"\'<,>.?/ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+={[}]|\\:;\"\'<,>.?/ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+={[}]|\\:;\"\'<,>.?/ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+¤ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+={[}]|\\:;\"\'<,>.?/ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+={[}]|\\:;\"\'<,>.?/ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+={[}]|\\:;\"\'<,>.?/ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+¤ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+={[}]|\\:;\"\'<,>.?/ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+={[}]|\\:;\"\'<,>.?/ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+={[}]|\\:;\"\'<,>.?/ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~`!@#$%^&*()_-+¤";
   int  loop = 0;
   int  start = 0;
   int  end = 0;
   int  diff = 0;
   _kernel_swi_regs r;
   printf("Testing old (C) method...\n");
   _kernel_swi(OS_ReadMonotonicTime, &r, &r);
   start = r.r[0];
   for (loop = 0; loop < 1000; loop++)
      o_lowercase(s);
   _kernel_swi(OS_ReadMonotonicTime, &r, &r);
   end = r.r[0];
   diff = end - start;
   printf("Time taken for 1000 iterations was %d centiseconds.\n\n", diff);
   printf("Testing new (assembler) method...\n");
   _kernel_swi(OS_ReadMonotonicTime, &r, &r);
   start = r.r[0];
   for (loop = 0; loop < 1000; loop++)
      lowercase(s);
   _kernel_swi(OS_ReadMonotonicTime, &r, &r);
   end = r.r[0];
   diff = end - start;
   printf("Time taken for 1000 iterations was %d centiseconds.\n\n", diff);
   return 0;
}
void o_lowercase(char string[])
{
   int  i = 0;
   while ( string[i] )
   {
      string[i] = tolower(string[i]);
      i++;
   }
   return;
}
; lowercase() in assembler...
;
;
; This code is written for objasm 2.00 or later.
;
; Other assemblers may need code modifications, or just remove
; the macro definition and references to it.
        AREA |C$$code|, CODE, READONLY
        ; This macro sets up the APCS backtrace 'name'
        MACRO
        HEAD     $name
        =        $name, 0
        ALIGN
        &        &FF000000 :OR: (:LEN: $name + 4 :AND: -4)
        MEND
; APCS registers
a1      RN      0
R0      RN      0
a2      RN      1
R1      RN      1
a3      RN      2
R2      RN      2
a4      RN      3
R3      RN      3
v1      RN      4
R4      RN      4
v2      RN      5
v3      RN      6
v4      RN      7
v5      RN      8
v6      RN      9
sl      RN      10
fp      RN      11
ip      RN      12
sp      RN      13
lr      RN      14
pc      RN      15
        EXPORT  |lowercase|
; R0 = Pointer to string (set on entry)
; R1 = Byte read from string
; R2 = Pointer to lowercase table
        HEAD     ("lowercase")
|lowercase|
        MOV      ip, sp
        STMFD    sp!, {a1, fp, ip, lr, pc}
        SUB      fp, ip, #4
        STMFD    sp!, {v6}
        MOV      R1, R0            ; Preserve string pointer
        SWI      &43040            ; "Territory_Number"
        SWI      &43057            ; "Territory_LowerCaseTable"
        MOV      R2, R0            ; Set lowercase table pointer
        MOV      R0, R1            ; Restore string pointer
lowercase_loop
        LDRB     R1, [R0]          ; Load character from R0
        CMP      R1, #0            ; Is it a null byte?
        LDMEQEA  fp, {fp, sp, pc}^ ; Return if null (end of string)
        LDRB     R1, [R2, R1]      ; Convert to indexed lowercase character
        STRB     R1, [R0], #1      ; Store character, increment offset pointer
        B        lowercase_loop    ; Mulberry bushes
        END
# Project: lowertest 
.SUFFIXES:   .c .s .o
CCflags      = -c -depend !depend -I,C: -throwback -fa
Linkflags    = -aif -o $@
ObjAsmflags  = -depend !depend -Stamp -quit -CloseExec
c_files      = o.runit
asm_files    = o.lower
libraries    = C:o.stubs
@.lowertest: $(c_files) $(asm_files)
             link $(linkflags) $(asm_files) $(c_files) $(libraries)
.c.o:;       cc $(ccflags) $< -o $@
.s.o:;       objasm $(objasmflags) -from $< -to $@
Anyway, now for the results of the international jury...
*lowertest Testing old (C) method... Time taken for 1000 iterations was 949 centiseconds. Testing new (assembler) method... Time taken for 1000 iterations was 284 centiseconds.That was measured on my A3000, so it will run quicker on later machines. However, it is clear to see, the C method takes three times as long.