; BuildString  0.15  04-10-2019

BS_Scratchspace EQU 16

BuildString                            ROUT
; Function:     Build a string with argument substitution, not unlike C's sprintf function, but a lot simpeler, to a
;               default buffer and with control characters (i.e. below space) acting as substitution triggers:
;                Ctrl+C = single ASCII character
;                Ctrl+D = decimal number with sign
;                Ctrl+L = string of limited length (defined by next argument)
;                Ctrl+O = octal number
;                Ctrl+S = string
;                Ctrl+U = unsigned decimal number
;                Ctrl+X = hexadecimal number (2, 4, 6 or 8 bytes wide and always in upper case)
;               If the numeric codes (D, O, U or X) are followed by Ctrl+L or Ctrl+R, the byte following that is the field width and
;               the text will be padded with spaces on the left (Ctrl+L) or on the right (Ctrl+R).
;               Carriage returns are ignored.
; Entry   :     r0  = pointer to template
;               r1  = pointer to argument number 1
;               r.  = pointer to argument number . (max. 11)
;               r12 = pointer to start of variable block
; Exit    :     r0  = pointer to resulting string
;               r1  = length of resulting string
; APCS    :     no
; Alters  :     r0,r1,lr
; Flags   :     V clear - others undefined
; Requires:     StringOctal         - external routine
;               Strings             - offset of buffer (256 bytes minimum) in workspace relative to r12
;               X                   - &20000
;               OS_ConvertHex2      - &D1
;               OS_ConvertHex4      - &D2
;               OS_ConvertHex6      - &D3
;               OS_ConvertHex8      - &D4
;               OS_ConvertCardinal4 - &D8
;               OS_ConvertInteger4  - &DC
        STMFD   sp!, {r1-fp, lr}
        MOV     fp, sp                           ; get pointer to first argument on stack
        SUB     sp, sp, #BS_Scratchspace
        ADD     sl, wp, #Strings                 ; get pointer to output (string buffer)
        STR     sl, [fp], #4                     ; store on stack, overwriting r1
        MOV     r9, r0                           ; get pointer to template from r0
        MOV     r0, r1                           ; get first argument from r1
        B       %f0010
BuildErrorString
; Function:     Build a string with argument substitution.
;               Identical to BuildString, but with the first word - the error number - transferred before scanning starts.
        STMFD   sp!, {r1-fp, lr}
        MOV     fp, sp                           ; get pointer to first argument on stack
        SUB     sp, sp, #BS_Scratchspace
        ADD     sl, wp, #Strings                 ; get pointer to output (string buffer)
        STR     sl, [fp], #4                     ; store on stack, overwriting r1
        MOV     r9, r0                           ; get pointer to template from r0
        LDR     r0, [r9], #4                     ; copy first word from template to output
        STR     r0, [sl], #4
        MOV     r0, r1                           ; get first argument from r1
        B       %f0010
BuildStringAtR0
; Function:     Build a string with argument substitution.
;               Identical to BuildString, but with a pointer to an output buffer in r0 and all other arguments shifted one register.
        STMFD   sp!, {r1-fp, lr}
        MOV     fp, sp                           ; get pointer to first argument on stack
        SUB     sp, sp, #BS_Scratchspace
        MOV     sl, r0                           ; get pointer to output from r0
        STR     sl, [fp], #4                     ; store on stack, overwriting r1
        MOV     r9, r1                           ; get pointer to template from r1
0000    LDR     r0, [fp], #4                     ; get next argument from stack
0010    LDRB    lr, [r9], #1
        CMP     lr, #0
        CMPNE   lr, #10
        CMPNE   lr, #' '                         ; character closing nul, line feed or otherwise not below space?
        BHS     %f0020                           ; then simply copy over
        TEQ     lr, #13
        BEQ     %b0010                           ; ignore carriage returns
        MOV     r3, #512                         ; reasonable default limit
        TEQ     lr, #'C'-64
        TEQNE   lr, #'S'-64
        BEQ     %f0040
        TEQ     lr, #'L'-64
        BEQ     %f0030
        TEQ     lr, #'D'-64
        BEQ     %f0060
        TEQ     lr, #'O'-64
        BEQ     %f0070
        TEQ     lr, #'U'-64
        BEQ     %f0080
        TEQ     lr, #'X'-64
        BEQ     %f0090
0020    STRB    lr, [sl], #1
        CMP     lr, #1                           ; stop at null
        BHS     %b0010
        ADD     sp, sp, #BS_Scratchspace
        LDR     r0, [sp], #4                     ; get pointer to output buffer from stack
        SBC     r1, sl, r0                       ; subtract 1 extra (as C=0) to discard the null
        LDMFD   sp!, {r2-fp, pc}
0030    LDR     r3, [fp], #4                     ; get next argument from stack to use as length for Ctrl+L
0040    CMP     r0, #0                           ; Ctrl+C, Ctrl+L or Ctrl+S? then argument may not be 0 or -1 (if it is, skip)
        CMNNE   r0, #1
        BEQ     %b0000
        TEQ     lr, #'C'-64
        STREQB  r0, [sl], #1                     ; Ctrl+C? store character
        BEQ     %b0000
0045    MOV     r4, #-1
0050    LDRB    lr, [r0], #1                     ; else store string
        TEQ     lr, #13
        BEQ     %f0052
        SUBS    r3, r3, #1                       ; limited to r3 characters
        CMPPL   lr, #9                           ; stop at carriage return or character below tab
        STRPLB  lr, [sl], #1
        BPL     %b0050
0052    CMP     r4, #0
        BLE     %b0000
        MOV     lr, #' '                         ; pad with spaces to the right
0055    SUBS    r3, r3, #1
        SUBPLS  r4, r4, #1                       ; unless the width of the field has already been used or wasn't specified
        STRPLB  lr, [sl], #1
        BPL     %b0055
        B       %b0000
0060    MOV     r1, sp                           ; Ctrl+D
        MOV     r2, #BS_Scratchspace
        SWI     X+OS_ConvertInteger4
        B       %f0100
0070    MOV     r1, sp                           ; Ctrl+O
        MOV     r2, #BS_Scratchspace
        BL      StringOctal
        B       %f0100
0080    MOV     r1, sp                           ; Ctrl+U
        MOV     r2, #BS_Scratchspace
        SWI     X+OS_ConvertCardinal4
        B       %f0100
0090    MOV     r1, sp                           ; Ctrl+X
        MOV     r2, #BS_Scratchspace
        TST     r0, #&FF000000
        BEQ     %f0092
        SWI     X+OS_ConvertHex8
        B       %f0100
0092    TST     r0, #&FF0000
        BEQ     %f0094
        SWI     X+OS_ConvertHex6
        B       %f0100
0094    TST     r0, #&FF00
        BEQ     %f0096
        SWI     X+OS_ConvertHex4
        B       %f0100
0096    SWI     X+OS_ConvertHex2
0100    RSB     r2, r2, #BS_Scratchspace
        SUB     r3, r3, r2
        LDRB    lr, [r9]                         ; get character following control char
        TEQ     lr, #'R'-64
        TEQNE   lr, #'L'-64
        BNE     %b0045                           ; Ctrl+L or Ctrl+R?
        ADD     r9, r9, #1
        LDRB    r4, [r9], #1                     ; then next byte holds field width (0-255)
        SUB     r1, r1, r4                       ; subtract width from end of string
        SUBS    r4, r0, r1                       ; result the same as or greater than start?
        BLS     %b0050                           ; then no more room for padding - store string
        TEQ     lr, #'R'-64
        BEQ     %b0050                           ; same with right padding
        MOV     lr, #' '
0110    SUBS    r3, r3, #1
        SUBPLS  r4, r4, #1
        STRPLB  lr, [sl], #1                     ; else insert spaces
        BPL     %b0110
        B       %b0050                           ; followed by string

        INCLUDE Sources:StringOctal

        END
