;    s.PrintDble
;
;******************************************************************************
;
;    This file assembles to a linkable object exporting one procedure which
;    may be useful in several programs, and obeys the ARM procedure call
;    standard, so may be called not only from assembler but also from high
;    level languages. The object file is intended to be incorporated in an
;    example linkable library with the LibFile tool.
;
;    Points demonstrated here include:
;       - Use of the LibFile tool to construct linkable libraries
;       - Non-leal and leaf procedures obeying the APCS
;
;    The procedure exported is print_double, which prints in n.nnnnExxx
;    from a double precision fp number contained in r0,r1 (ie. 5sf).
;    It has the C prototype:  void print_double(double number);
;
;    *** NOTE *** print_double must NOT be called from SVC mode, for
;    example from a relocatable module SVC code.
;

; Use the GET directive to include a list of SWI names as if typed here

        GET     ^.AsmHdrs.h.SWINames

; Area name C$$code advisable if wanted to link with C output

        AREA    |C$$code|, CODE, READONLY

        EXPORT  |print_double|

; APCS specifies input arguments in lowest registers - since 2 words here use r0,r1
; r0 - r3 and f0 - f3 can be altered
; This is a non-leaf procedure, as it calls print_digits, so it sets up a stack
; frame to conform to the APCS

|print_double|

        MOV     ip, sp
        STMFD   sp!, {r0,r1,fp,ip,lr,pc}
        SUB     fp, ip, #4
        LDFD    f0, [sp], #8               ; pick up floating point number
        STFP    f0, [sp, #-12]!            ; then stack it in packed decimal

        CMF     f0, #0                     ; check for zero
        MOV     r0, #"0"
        BNE     %FT05
        SWI     OS_WriteC
        B       ret

05      MOV     r0, sp
        BL      packed_expand

        LDRB    r3, [sp]                   ; get sign byte
        TST     r3, #&80                   ; number negative?
        MOVNE   r0, #"-"
        SWINE   OS_WriteC                  ; if so, print -

        ADD     r2, sp, #5
        LDRB    r0, [r2], #1               ; print mantissa
        SWI     OS_WriteC
        MOV     r0, #"."
        SWI     OS_WriteC
        LDRB    r0, [r2], #1
        SWI     OS_WriteC
        LDRB    r0, [r2], #1
        SWI     OS_WriteC
        LDRB    r0, [r2], #1
        SWI     OS_WriteC
        LDRB    r0, [r2], #1
        SWI     OS_WriteC

        MOV     r0, #"E"                   ; print exponent
        SWI     OS_WriteC
        TST     r3, #&40                   ; exponent negative?
        MOVNE   r0, #"-"
        SWINE   OS_WriteC                  ; if so, print -
        LDRB    r0, [sp, #1]!
        CMP     r0, #"0"
        SWINE   OS_WriteC
        LDRB    r0, [sp, #1]!
        CMPEQ   r0, #"0"
        SWINE   OS_WriteC
        LDRB    r0, [sp, #1]!
        CMPEQ   r0, #"0"
        SWINE   OS_WriteC
        LDRB    r0, [sp, #1]!
        SWI     OS_WriteC

ret
 [ {CONFIG}=26
        LDMEA   fp, {fp,sp,pc}^
 |
        LDMEA   fp, {fp,sp,pc}
 ]


; Take a packed decimal fp number pointed to by r0 and expand it into characters
; Assumes character form will take no more space than packed - 7sf max, here 5
; Since this is a leaf procedure (calls no others) it doesn't set up a stack frame

packed_expand

        LDMIA   r0, {r1, r2}
        MOV     r3, r1, LSR #24            ; preserve sign word
        STRB    r3, [r0]

        MOV     r2, r2, LSR #20            ; put mantissa nibbles in r2
        ORR     r2, r2, r1, LSL #12
        BIC     r2, r2, #&ff000000

        LDR     r3, = &66666b              ; rounding pattern
        ADD     r2, r2, r3
        MOV     r2, r2, LSR #4             ; then lose rounding nibble
        TST     r2, #&100000               ; have we rounded up exponent?
        MOVNE   r2, r2, LSR #4             ; yes, so shift mantissa
        ADDNE   r2, r2, #&60000            ; and prepare extra digit for adjustment

        MOV     r1, r1, LSR #12            ; put exponent nibbles in r1
        ADDNE   r1, r1, #1                 ; increment exponent if needed
        TSTNE   r1, #&40000
        SUBNE   r1, r1, #2                 ; or decrement if exponent negative
        BIC     r1, r1, #&f0000
        MOV     r3, r3, LSR #8             ; = &6666 decimalisation pattern
        ADD     r1, r1, r3                 ; exponent cannot overflow 4 digits

        CMP     r1, r3                     ; if zero exponent
        LDREQB  r3, [r0]
        BICEQ   r3, r3, #&40               ; make sure positive exponent
        STREQB  r3, [r0]

        ADD     r0, r0, #9                 ; place to put last char. of mantissa
mant_loop
        ANDS    r3, r2, #&f                ; get digit nibble
        SUBNE   r3, r3, #6                 ; remove decimalisation pattern
        ADD     r3, r3, #"0"               ; turn into ASCII
        STRB    r3, [r0], #-1              ; and write as a byte
        MOVS    r2, r2, LSR #4             ; next nibble
        BNE     mant_loop

exp_loop
        ANDS    r3, r1, #&f                ; get digit nibble
        SUBNE   r3, r3, #6                 ; remove decimalisation pattern
        ADD     r3, r3, #"0"               ; turn into ASCII
        STRB    r3, [r0], #-1              ; and write as a byte
        MOVS    r1, r1, LSR #4             ; next nibble
        BNE     exp_loop

 [ {CONFIG}=26
        MOVS    pc, lr
 |
        MOV     pc, lr
 ]

        END
