; allocate memory for the given string - this should be a 256 byte buffer at the end of the
; array buffer memory. Ensure that the allocated memory is cleared.

.allocate_memory_for_string
          STMFD   R13!,{R0-R11,R14}
          LDR     R1,offset_of_next_string
          ADD     R2,R1,R12
          STR     R2,[R0]
          ADD     R3,R1,#256
          STR     R3,offset_of_next_string
          MOV     R0,#0
          ADD     R1,R2,#256
.iloop7
          STRB    R0,[R2],#1
          CMP     R2,R1
          BLT     iloop7
          LDMFD   R13!,{R0-R11,R15}^

; concatenate the two null terminated strings pointed to by R0 and R1 and return the result
; as a null terminated string placed in the buffer pointed to by R2.

.concatenate_strings
          STMFD   R13!,{R0-R11,R14}
.iloop6
          LDRB    R3,[R0],#1
          CMP     R3,#0
          BEQ     ilabel3
          STRB    R3,[R2],#1
          B       iloop6
.ilabel3
          LDRB    R3,[R1],#1
          STRB    R3,[R2],#1
          CMP     R3,#0
          BNE     ilabel3
          LDMFD   R13!,{R0-R11,R15}^

; place the null terminated string (the address of which is passed in R0) into the buffer whose
; address is passed in R1.

.library_routine_place_string_in_buffer
          STMFD   R13!,{R0-R11,R14}
.iloop1
          LDRB    R2,[R0],#1
          STRB    R2,[R1],#1
          CMP     R2,#0
          BNE     iloop1
          LDMFD   R13!,{R0-R11,R15}^

; compare the two strings pointed to by R0 and R1. Return -1 in R2 if they are identical OR 0
; if they are not.

.library_routine_compare_strings_for_equivalence
          STMFD   R13!,{R0,R1,R3-R11,R14}
          MVN     R2,#0
.iloop2
          LDRB    R3,[R0],#1
          LDRB    R4,[R1],#1
          CMP     R3,R4
          MOVNE   R2,#0
          CMP     R3,#0
          BNE     iloop2
          LDMFD   R13!,{R0,R1,R3-R11,R15}^
                    
; return the length of the null terminated string pointed to by R0 in R0.

.library_routine_LEN_function
          STMFD   R13!,{R1-R11,R14}
          MOV     R1,#0
.iloop3
          LDRB    R2,[R0],#1
          CMP     R2,#0
          ADDNE   R1,R1,#1
          BNE     iloop3
          MOV     R0,R1
          LDMFD   R13!,{R1-R11,R15}^
          
; place the leftmost R1 number of characters of the null terminated string pointed to by R0 in
; the buffer pointed to by R2. Note that it is possible that R1 is an invalid number (ie. it
; may be a negative number (other than -1), greater than the length of the string or a zero).
; Return the address of the resulting string in R0. If a value of -1 is passed in R1 then
; return the string minus the rightmost character.
                    
.library_routine_LEFT_function
          STMFD   R13!,{R1-R11,R14}
          MOV     R3,R0                            ; R3 = (preserved) pointer to string
          MOV     R4,R2                            ; R4 = (preserved) pointer to resulting $
          BL      library_routine_LEN_function
          CMP     R1,R0
          MOVGT   R1,R0
          CMN     R1,#1                            ; does R1 = -1 (special value)
          SUBEQ   R1,R0,#1                         ; if so then set to len of string -1
          CMP     R1,#0
          MOVLT   R1,#0                            ; R1 = valid number of characters
          ADD     R0,R3,R1                         ; R0 = end pointer (last char + 1)
.iloop4
          CMP     R3,R0
          BEQ     ilabel1
          LDRB    R5,[R3],#1
          STRB    R5,[R2],#1
          B       iloop4
.ilabel1
          MOV     R5,#0
          STRB    R5,[R2],#1
          MOV     R0,R4
          LDMFD   R13!,{R1-R11,R15}^
          
; place the rightmost R1 number of characters of the null terminated string pointed to by R0 in
; the buffer pointed to by R2. Note that it is possible that R1 is an invalid number (ie. it
; may be a negative number, greater than the length of the string or a zero). Return the
; address of the resulting string in R0.
                    
.library_routine_RIGHT_function
          STMFD   R13!,{R1-R11,R14}
          MOV     R3,R0                            ; R3 = (preserved) pointer to string
          MOV     R4,R2                            ; R4 = (preserved) pointer to resulting $
          BL      library_routine_LEN_function
          CMP     R1,R0
          MOVGT   R1,R0
          CMP     R1,#0
          MOVLT   R1,#0                            ; R1 = valid number of characters
          ADD     R0,R3,R0                         ; R0 = end pointer (last char + 1)
          SUB     R1,R0,R1                         ; R1 = pointer to first required character
.iloop5
          CMP     R1,R0
          BEQ     ilabel2
          LDRB    R3,[R1],#1
          STRB    R3,[R2],#1
          B       iloop5
.ilabel2
          MOV     R3,#0
          STRB    R3,[R2],#1
          MOV     R0,R4
          LDMFD   R13!,{R1-R11,R15}^
          
; return a pointer to a copy of the given section of the given string pointed to by R0. R1
; should hold the position of the first character, R2 the number of characters and R3 a pointer
; to where the resulting string should be placed. All registers should be preserved. Note that
; the values in R1 and R2 could (potentially) be invalid.

.library_routine_MID_function
          STMFD   R13!,{R0-R11,R14}
          MOV     R4,R0                            ; R4 = preserved pointer to string
          BL      library_routine_LEN_function     ; R0 = length of string
          CMP     R1,R0
          MOVGT   R1,R0                            ; ensures R1 isn't too high a number
          CMP     R1,#0
          MOVLE   R1,#1                            ; ensures R1 isn't too small
          ADD     R5,R1,R2
          SUB     R5,R5,#1                         ; position of last character in substring
          CMP     R5,R0
          SUBGT   R2,R0,R1
          ADDGT   R2,R2,#1                         ; ensures R2 not too large
          CMP     R2,#0
          MOVLT   R2,#0                            ; ensures R2 not too small
          MOV     R0,R4                            ; restore original string pointer
          ADD     R4,R0,R1
          SUB     R4,R4,#1                         ; R4 = pointer to first character
          ADD     R5,R4,R2                         ; R5 = pointer to last character + 1
.iloop8
          CMP     R4,R5
          BEQ     ilabel4
          LDRB    R6,[R4],#1
          STRB    R6,[R3],#1
          B       iloop8
.ilabel4
          MOV     R6,#0
          STRB    R6,[R3]                          ; null terminate the resulting string
          LDMFD   R13!,{R0-R11,R15}^
          
; return the position of the first instance of the given substring in the given string OR 0 if
; not found. On entry R0 = pointer to string, R1 = pointer to substring and R2 = first
; character to begin searching from. On exit R0 is the  result register.

.library_routine_INSTR_function
          STMFD   R13!,{R1-R11,R14}
          MOV     R3,R0                            ; R3 = preserved pointer to string
          BL      library_routine_LEN_function     ; R0 = length of string
          CMP     R2,R0
          MOVGT   R2,R0                            ; ensures R2 isn't too large
          CMP     R2,#0
          MOVLE   R2,#1                            ; ensures R2 isn't too small
          ADD     R4,R3,R0                         ; pointer to last character + 1
          MOV     R0,R3                            ; restore string pointer
          ADD     R0,R0,R2
          SUB     R0,R0,#1                         ; set pointer to start of search
.iloop9
          CMP     R0,R4                            ; has end of string been reached ?
          MOVEQ   R0,#0                            ; if so then set position to 0 and exit
          BEQ     ilabel5                          
          BL      library_routine_compare_start_of_string_with_substring
          CMN     R2,#1                            
          SUBEQ   R0,R0,R3                         ; was a match found ?
          ADDEQ   R0,R0,#1                         ; if so then set position and exit loop.
          BEQ     ilabel5
          ADD     R0,R0,#1
          B       iloop9
          
.ilabel5
          LDMFD   R13!,{R1-R11,R15}^

; check to see if the beginning of the string pointed to by R0 is identical to the substring
; pointed to by R1. If it is then return -1 in R2, otherwise return 0 in R2.

.library_routine_compare_start_of_string_with_substring
          STMFD   R13!,{R0,R1,R3-R11,R14}
.iloop10
          LDRB    R2,[R0],#1
          LDRB    R3,[R1],#1
          CMP     R2,R3
          MOVNE   R5,#0
          BNE     ilabel6
          LDRB    R3,[R1]
          CMP     R3,#0
          BNE     iloop10
          MVN     R5,#0
.ilabel6
          MOV     R2,R5
          LDMFD   R13!,{R0,R1,R3-R11,R15}^
 
; the following subroutine divides two numbers. On entry R0=dividend, R1=divisor. On exit
; R0=quotient, R1=remainder.
         
.library_routine_divide
          STMFD   R13!,{R2-R11,R14}
          ANDS    R6,R0,#1<<31
          RSBMI   R0,R0,#0
          EOR     R5,R6,R1
          CMP     R1,#0
          RSBMI   R1,R1,#0
          MOV     R2,#0
          MOV     R3,#0
          MOV     R4,#1<<31
.iloop11
          MOVS    R0,R0,ASL#1
          ADC     R2,R2,R2
          CMP     R2,R1
          SUBGE   R2,R2,R1
          ORRGE   R3,R3,R4
          MOVS    R4,R4,LSR#1
          BNE     iloop11
          CMP     R5,#0
          RSBMI   R3,R3,#0
          MOV     R0,R3
          MOV     R1,R2
          LDMFD   R13!,{R2-R11,R15}^
          
; check to see if the string pointed to by R0 is greater than the string pointed to by R1.
; Return -1 in R2 if it is, 0 otherwise.

.library_routine_compare_strings_for_greater_than
          STMFD   R13!,{R0,R1,R3-R11,R14}
.iloop12
          LDRB    R2,[R0],#1
          LDRB    R3,[R1],#1
          ADD     R4,R2,R3
          CMP     R4,#0
          MOVEQ   R2,#0
          BEQ     ilabel7
          CMP     R2,R3
          BEQ     iloop12
          MVNGT   R2,#0
          MOVLE   R2,#0
.ilabel7
          LDMFD   R13!,{R0,R1,R3-R11,R15}^

; set the rightmost part of the null terminated string pointed to by R0 to the null terminated
; string pointed to by R1. This subroutine mimics the BASIC RIGHT$(..= command.

.library_routine_set_right_part_of_string
          STMFD   R13!,{R0-R11,R14}
          MOV     R2,R0                            ; R2 = preserved string pointer
          BL      library_routine_LEN_function
          MOV     R3,R0                            ; R3 = length of first string
          MOV     R0,R1
          BL      library_routine_LEN_function     ; R0 = number of characters to replace
          CMP     R0,R3
          BGT     ilabel8
          ADD     R4,R2,R3
          SUB     R4,R4,R0                         ; R4 = pointer to first char to replace
.iloop13
          LDRB    R5,[R1],#1
          STRB    R5,[R4],#1
          CMP     R5,#0
          BNE     iloop13
.ilabel8
          LDMFD   R13!,{R0-R11,R15}^

; set the leftmost part of the null terminated string pointed to by R0 to the null terminated
; string pointed to by R1. This subroutine mimics the BASIC LEFT$(..= command.

.library_routine_set_left_part_of_string
          STMFD   R13!,{R0-R11,R14}
          MOV     R2,R0                            ; R2 = preserved string pointer
          BL      library_routine_LEN_function
          MOV     R3,R0                            ; R3 = length of first string
          MOV     R0,R1
          BL      library_routine_LEN_function     ; R0 = number of characters to replace
          CMP     R0,R3
          BGT     ilabel9
.iloop14
          LDRB    R5,[R1],#1
          CMP     R5,#0
          STRNEB  R5,[R2],#1
          BNE     iloop14
.ilabel9
          LDMFD   R13!,{R0-R11,R15}^

; beginning with the R1 character set the given section of the string pointed to by R0 to the
; string pointed to by R2.

.library_routine_set_middle_part_of_string
          STMFD   R13!,{R0-R11,R14}
          MOV     R3,R0                            ; R3 = preserved string pointer
          BL      library_routine_LEN_function     ; R0 = length of first string
          CMP     R1,R0
          BGT     ilabel10                         ; ensure that R1 not too big
          CMP     R1,#0
          MOVLE   R1,#1                            ; ensure that R1 not too small
          ADD     R0,R3,R1
          SUB     R0,R0,#1                         ; R0 = pntr to section of str to replace
          MOV     R1,R2                            ; R1 = pntr to replacement string
          BL      library_routine_set_left_part_of_string
.ilabel10
          LDMFD   R13!,{R0-R11,R15}^

; retrieve the integer value from the required (two dimensional) integer array. On entry R0=
; x dimension, R1 = y dimension, R3 = offset to array's address (not the address of the array!)
; and R2 = offset to the arrays width. On exit R0 = value. All other registers should be
; preserved.

.library_routine_retrieve_integer_array_value
          STMFD   R13!,{R1-R11,R14}
          LDR     R2,[R12,R2]
          ADD     R2,R2,#1
          MOV     R2,R2,ASL#2
          MUL     R2,R1,R2
          MOV     R4,R0,ASL#2
          ADD     R4,R2,R4
          LDR     R1,[R12,R3]
          LDR     R0,[R1,R4]
          LDMFD   R13!,{R1-R11,R15}^
          
; retrieve the address of the string value from the required (two dimensional) string array.
; On entry R0 = x dimension, R1 = y dimension,R2 = offset to array's width and R3 = offset
; to array's address.

.library_routine_retrieve_string_array_value
          STMFD   R13!,{R1-R11,R14}
          LDR     R2,[R12,R2]
          ADD     R2,R2,#1
          MOV     R2,R2,ASL#8
          MUL     R2,R1,R2
          MOV     R4,R0,ASL#8
          ADD     R4,R2,R4
          LDR     R1,[R12,R3]
          ADD     R0,R1,R4
          LDMFD   R13!,{R1-R11,R15}^
          
; return the address of the given array's given element. On entry R0 and R1 are the x and y
; dimensions of the required element (respectively), R2 = offset to width of array and R4 =
; offset to address of array. On exit R0 = address of element.

.library_routine_address_of_integer_array_element
          STMFD   R13!,{R1-R11,R14}
          LDR     R2,[R12,R2]
          ADD     R2,R2,#1
          MOV     R2,R2,ASL#2
          MUL     R2,R1,R2
          MOV     R5,R0,ASL#2
          ADD     R5,R2,R5
          LDR     R1,[R12,R4]
          ADD     R0,R1,R5
          LDMFD   R13!,{R1-R11,R15}^

; write the given string to the given *open* file. On entry R0 = pointer to string, R1 =
; file handle.

.library_routine_PRINT_string_to_file
          STMFD   R13!,{R0-R11,R14}
          MOV     R2,R0
          MOV     R0,#0
          SWI     "OS_BPut"
          MOV     R0,R2
          BL      library_routine_LEN_function
          SWI     "OS_BPut"
          ADD     R3,R2,R0
          SUB     R3,R3,#1
.iloop15
          LDRB    R0,[R3],#-1
          SWI     "OS_BPut"
          CMP     R3,R2
          BGE     iloop15
          LDMFD   R13!,{R0-R11,R15}^
          
; write the given integer to the given *open* file. On entry R2 = integer to be written, R1 =
; file handle.

.library_routine_PRINT_integer_to_file
          STMFD   R13!,{R0-R11,R14}
          MOV     R0,#&40
          SWI     "OS_BPut"
          AND     R0,R2,#%11111111000000000000000000000000
          MOV     R0,R0,ASR#24
          SWI     "OS_BPut"
          AND     R0,R2,#%111111110000000000000000
          MOV     R0,R0,ASR#16
          SWI     "OS_BPut"
          AND     R0,R2,#%1111111100000000
          MOV     R0,R0,ASR#8
          SWI     "OS_BPut"
          AND     R0,R2,#%11111111
          SWI     "OS_BPut"
          LDMFD   R13!,{R0-R11,R15}^

; read a string from the given file and return a pointer to it. On entry R1 = file handle, R2=
; pointer to string buffer. On exit R0 = address of string buffer.

.library_routine_INPUT_string_from_file
          STMFD   R13!,{R1-R11,R14}
          SWI     "OS_BGet"
          SWI     "OS_BGet"
          LDR     R2,[R12,R2]
          ADD     R3,R2,R0
          MOV     R4,#0
          STRB    R4,[R3],#-1
.iloop16
          SWI     "OS_BGet"
          STRB    R0,[R3],#-1
          CMP     R3,R2
          BGE     iloop16
          MOV     R0,R2
          LDMFD   R13!,{R1-R11,R15}^
          
; read in an integer from the given open file. On entry R1 = file handle. On exit R0 = integer

.library_routine_INPUT_integer_from_file
          STMFD   R13!,{R1-R11,R14}
          MOV     R2,#0
          SWI     "OS_BGet"
          SWI     "OS_BGet"
          ADD     R2,R2,R0,ASL#3
          SWI     "OS_BGet"
          ADD     R2,R2,R0,ASL#2
          SWI     "OS_BGet"
          ADD     R2,R2,R0,ASL#1
          SWI     "OS_BGet"
          ADD     R0,R2,R0
          LDMFD   R13!,{R1-R11,R15}^

; convert the integer passed in R0 to a string and place at the address passed in R1. Pad with
; leading spaces so as the string is 10 characters long.

.library_routine_convert_integer_to_decimal_string
          STMFD   R13!,{R0-R11,R14}
          MOV     R2,#12
          SWI     "OS_BinaryToDecimal"
          MOV     R3,#0
          STRB    R3,[R1,R2]
          CMP     R2,#10
          BEQ     ilabel11
          ADD     R6,R1,R2                       ; R6 = source pointer
          SUB     R4,R6,R1                       ; R4 = length of string
          ADD     R5,R1,#10                      ; R5 = destination pointer
.iloop17
          LDRB    R7,[R6],#-1
          STRB    R7,[R5],#-1
          CMP     R6,R1
          BGE     iloop17
          MOV     R7,#32                         ; ASCII code for a space character
.iloop18
          STRB    R7,[R1],#1                     ; write space character to buffer
          CMP     R1,R5                          ; check to see if padding done
          BLE     iloop18
.ilabel11
          LDMFD   R13!,{R0-R11,R15}^