SX Microcontroller Radix Math Method

Binary to BCD packed and ASCII, 24 bit to 8 or 9 digits with sign, and decimal point

from Tony Kübek tony.kubek at flintab.com

...it's for 24 bit signed variable, then I cannot output a variable at a time ( supposed to go inside an int handler ), therefore I'm using a 9 byte string to store the result, furthermore for the decimal point and supressing of leading zeroes I needed 1 byte and 2 bit variables. ... Its a bit of 'bloatware', requires about 290 ! instructions but thats efficient enough for me, I might get back to hammer on it when I've finished off the other bits I'm starting with now. I think it goes around 480 instruction executed worst case, and all the way down to 250 something for best case. Also I added a minor conditional that makes execution a tad faster for 20 bit numbers ( as my app will have about 99 % in this range ) to the expence of more code and additional execution for larger numbers.

Translated and optimized for the Scenix SX by Nikolai Golovchenko

;**********************************************************************
;                                                                     *
;    Filename:          MathTest.asm 24bit signed binary->ASCII w. dp *
;    Date:              2000-03-31                                    *
;    File Version:      0.1B                                          *
;                                                                     *
;    Author:            Tony Kübek                                    *
;    Company:                                                         *
;                                                                     *
;                                                                     *
;**********************************************************************
;                                                                     *
;    Notes:  Tests of binary to ASCII coversion                       *
;            Routines from techref.massmind.org                       *
;            Originators(16bit): Scott Dattalo and Nikolai Golovchenko*
;            Routine takes 24 bit signed binary number and coverts to *
;            8/9 byte ASCII(true!) decimal string with decimal point  *
;            and minus sign as the first byte ( if negative ).        *
;            24 bit variabel is stored in last three string bytes     *
;            These are 'destroyed' during processing                  *
;            Leading zeroes are surpressed (conv. to space)           *
;            Resources needed:                                        *
;                       DpPlacement 1 byte + 1 bit                    *
;                       PrevIsChar 1 bit ,prev. zero was surpressed   *
;                       ASCII string of 9 bytes                       *
;**********************************************************************
                DEVICE  SX28
                RESET   Start

TEST_SMALL_NUMBER EQU 1         ; if defined then tests are performed
                                ; to see number is less than +/-1048575 ( 20 bit )
                                ; makes routine a little faster for thoose numbers
                                ; to the expence of more code
                                ; and adds some cycles to all numbers above if used
                                ; but can deduct upto 62 cycles on smaller numbers

DECIMAL_POINT   EQU     0x2E    ; '.'
BYTE_OFFSET     EQU     0x30    ; start decimal ASCII numbers
SIGN_NEGATIVE   EQU     0x2D    ; '-'

                ORG     $10     ; start userram bank 0
BitVars         DS      1       ; used for dp-bit variable
OutPut          DS      9       ; 8 used if no dp, else 9
DpPlacement     DS      1       ; where to put dp, counted from start, min=1
                                ; note an added bonus here ! as this var is
                                ; located directly after the string and will
                                ; 'always' be = 0x00(after conversion),
                                ; you have an 'free' null terminator


_DpUsed         EQU     BitVars.0 ; decimal point has been placed
_PrevIsSpace    EQU     BitVars.1 ; previous byte/digit is 'converted' to space

Sample          EQU     OutPut+6  ; Note !! to save ram, 24 bit variable is stored
                                  ; in highest three byte in 9 byte string

; define constants to add/sub to the input number
; low/mid/high byte is relative to a 24 bit number
;LOW4M           EQU 4000000 & $FF   is zero!
MID4M           EQU (4000000 >> 8) & $FF
HIGH4M          EQU $3D                 ;(4000000 >> 16) & $FF
LOW1M           EQU 1000000 & $FF
MID1M           EQU (1000000 >> 8) & $FF
HIGH1M          EQU $0F                 ;(1000000 >> 16) & $FF
LOW300K         EQU 300000 & $FF
MID300K         EQU (300000 >> 8) & $FF
HIGH300K        EQU $04                 ;(300000 >> 16) & $FF
LOW100K         EQU 100000 & $FF
MID100K         EQU (100000 >> 8) & $FF
HIGH100K        EQU $01                 ;(100000 >> 16) & $FF
LOW30K          EQU 30000 & $FF
MID30K          EQU (30000 >> 8) & $FF
LOW10K          EQU 10000 & $FF
MID10K          EQU (10000 >> 8) & $FF
LOW3K           EQU 3000 & $FF
MID3K           EQU (3000 >> 8) & $FF
LOW1K           EQU 1000 & $FF
MID1K           EQU (1000 >> 8) & $FF
LOW300          EQU 300 & $FF
MID300          EQU (300 >> 8) & $FF

                ORG $0
Start

;************************ Tony test 24 bit->8/9 digit ASCII with decimal point
; code with TEST_SMALL_NUMBER: (about)290 instructions ( excluding variable setup code )
;
; True ASCII conversion, variable decimalpoint, surpress leading zeroes
; 24 bit variable is handled as SIGNED!
;************************************
        clr     BitVars

        ; dp=1->dp=7(no dp), adds approx 50 cycles, i.e. dp=1 least cycles
        mov     W, #1           ; note dp  = 1 = between first and second digit
                                ; dp = 7 between the last and second last digit
                                ; if dp >= 7, dp = 0 then last digit is = 0x00
        mov     DpPlacement, W

        mov     W, #BYTE_OFFSET ; preset with ASCII offset
        mov     OutPut, W
        mov     OutPut+1, W
        mov     OutPut+2, W
        mov     OutPut+3, W
        mov     OutPut+4, W
        mov     OutPut+5, W
        clrb    OutPut.4        ; clear bit, makes first char 'space'


        ; OutPut+6 - OutPut+8 will contain 24 bit sample, LSB in OutPut+8
        ; ** NOTE ** Sample = OutPut+6, Sample+1 = OutPut+7, Sample+2 = OutPut+8
        ; Sample is defined as a separate name as it makes the code easier to read


;       test number : 1235783 = 0x12DB47 - ok ! cycles approx: 301 -350 depending on dp placment
;       Note ! This with TEST_SMALL_NUMBER, without deduct  10 cycles
;       mov     W, #$47
;       mov     Sample+2, W
;       mov     W, #$DB
;       mov     Sample+1, W
;       mov     W, #$12
;       mov     Sample, W

;       test number : -1235783 = 0xED24B9 - ok ! cycles approx: 311 -360 depending on dp placment
;       Note ! This with TEST_SMALL_NUMBER, without deduct  10 cycles
;       mov     W, #$B9
;       mov     Sample+2, W
;       mov     W, #$24
;       mov     Sample+1, W
;       mov     W, #$ED
;       mov     Sample, W

;       test number : 60900 = 0x00EDE4 - ok ! cycles approx: 315 -371 depending on dp placment
;       Note ! This with TEST_SMALL_NUMBER, without add  63 cycles
;       mov     W, #$E4
;       mov     Sample+2, W
;       mov     W, #$ED
;       mov     Sample+1, W
;       mov     W, #$00
;       mov     Sample, W

;       test number : -60900 = 0xFF121C - ok ! cycles approx: 325 -381 depending on dp placment
;       Note ! This with TEST_SMALL_NUMBER, without add  56 cycles
;       mov     W, #$1C
;       mov     Sample+2, W
;       mov     W, #$12
;       mov     Sample+1, W
;       mov     W, #$FF
;       mov     Sample, W



;       test number : 231 = 0xE7 - ok !, cycles approx 244-302 depending on dp placement
;       NOTE ! With TEST_SMALL_NUMBER, without add 62 cycles

;       mov     W, #$E7
;       mov     Sample+2, W
;       mov     W, #$00
;       mov     Sample+1, W
;       mov     W, #$00
;       mov     Sample, W

;       test number : -1 = 0xFFFFFF - ok !, cycles approx:262-326 depending on dp placement
;       This with TEST_SMALL_NUMBER, without add 62 cycles
;

;       mov     W, #$FF
;       mov     Sample+2, W
;       mov     W, #$FF
;       mov     Sample+1, W
;       mov     W, #$FF
;       mov     Sample, W

;       test number : 1048575 = 0x0FFFFF - ok !, cycles approx: 297-348 depending on dp placement
;       this with TEST_SMALL_NUMBER, without add 8 cycles
;       mov     W, #$FF
;       mov     Sample+2, W
;       mov     W, #$FF
;       mov     Sample+1, W
;       mov     W, #$0F
;       mov     Sample, W

;       test number : 1000000 = 0x0F4240 - ok !, cycles approx: 291-350 depending on dp placement
;       this with TEST_SMALL_NUMBER, without add 8 cycles
;       mov     W, #$40
;       mov     Sample+2, W
;       mov     W, #$42
;       mov     Sample+1, W
;       mov     W, #$0F
;       mov     Sample, W

;       test number : 8388607 = 0x7FFFFF - ok !, cycles approx: 394-445 depending on dp placement
;       this with TEST_SMALL_NUMBER, without add 8 cycles
;       mov     W, #$FF
;       mov     Sample+2, W
;       mov     W, #$FF
;       mov     Sample+1, W
;       mov     W, #$7F
;       mov     Sample, W
;       test number : -7099999 = 0x93A9A1 - ok !, cycles approx:429-480 depending on dp placement
;       this with TEST_SMALL_NUMBER, without add 8 cycles
        mov     W, #$A1
        mov     Sample+2, W
        mov     W, #$A9
        mov     Sample+1, W
        mov     W, #$93
        mov     Sample, W


TEST_NEGATIVE
        sb      Sample.7        ; test if negative
IF      TEST_SMALL_NUMBER == 1
        jmp     TEST_TOPNIBBLE  ; nope
ELSE
        jmp     SUB4M           ; nope
ENDIF

        ; sample is negative
        ; make two's complement
        not     Sample+2
        not     Sample+1
        not     Sample
        mov     W, #1
        add     Sample+2, W
        snb     C
        add     Sample+1, W
        snb     C
        add     Sample, W

        mov     W, #SIGN_NEGATIVE
        mov     OutPut, W       ; first digit minus sign



IF      TEST_SMALL_NUMBER == 1  ; test for small numbers and skips some loops
                                ; use if number is often in 0-1048575 range ( negative or not)
                                ; i.e. 0x000000 - 0x0FFFFF ( 2.5 bytes 20 bits )

TEST_TOPNIBBLE
        ; test if top nibble is zero, i.e. number less than 1048576 ( decimal )
        ; if so we can skip to sub300k routine

        mov     W, Sample
        and     W, #$F0         ; Mask out top four bits
        sb      Z
        jmp     SUB4M           ; nope do full conversion

        ; this digit is zero, and dp is not yet set
        ; then downshift to space
        setb    _PrevIsSpace
        clrb    OutPut+1.4

        jmp     SUB300k         ; continue

ENDIF                           ; TEST_SMALL_NUMBER


SUB4M:
        ; subtract 4 million from sample
        mov     W, #4
        add     OutPut+1, W

        mov     W, #MID4M
        sb      C
        mov     W, #MID4M + 1
        sub     Sample+1, W

        mov     W, #HIGH4M
        sb      C
        mov     W, #HIGH4M + 1
        sub     Sample, W


        snb     C               ; overflow, i.e. negative
        jmp     SUB4M

ADD1M:
        ; add 1 million to sample
        dec     OutPut+1
        mov     W, #LOW1M
        add     Sample+2, W

        mov     W, #MID1M
        snb     C
        mov     W, #MID1M + 1
        add     Sample+1, W

        mov     W, #HIGH1M
        snb     C
        mov     W, #HIGH1M + 1
        add     Sample, W

        sb      C               ; done ?
        jmp     ADD1M

        ; test for leading zeroes
        mov     W, #BYTE_OFFSET
        mov     W, OutPut+1-w
        sb      Z
        jmp     SUB300k

        ; this digit is zero, and dp is not yet set
        ; then downshift to space
        setb    _PrevIsSpace
        clrb    OutPut+1.4


SUB300k:
        ; substract 300 thousand from sample
        mov     W, #3
        add     OutPut+3, W
        mov     W, #LOW300K
        sub     Sample+2, W

        mov     W, #MID300K
        sb      C
        mov     W, #MID300K + 1
        sub     Sample+1, W

        mov     W, #HIGH300K
        sb      C
        mov     W, #HIGH300K + 1
        sub     Sample, W


        snb     C
        jmp     SUB300k

ADD100k
        ; add 100 thousand to sample
        dec     OutPut+3
        mov     W, #LOW100K
        add     Sample+2, W

        mov     W, #MID100K
        snb     C
        mov     W, #MID100K + 1
        add     Sample+1, W

        mov     W, #HIGH100K
        snb     C
        mov     W, #HIGH100K + 1
        add     Sample, W

        sb      C       ; done
        jmp     ADD100k
IF      TEST_SMALL_NUMBER == 1  ; if this test enabled it is possible
                                ; to have an overflow here

        ; test for overflow ( Output+3 = 10 )

        mov     W, #BYTE_OFFSET+$0A
        mov     W, OutPut+3-w
        sb      Z               ; test if non zero
        jmp     ADD100k_TstDp


        ; over flow, result is = 10, second digit = 1, third = 0
        mov     W, #BYTE_OFFSET
        mov     OutPut+3, W
        sb      _DpUsed         ; test if dp already used
        setb    OutPut+1.0      ; if no dp used set this digit to one
        setb    OutPut+2.0      ; set also third digit to 1 ( in case dp is used )

        ; restore previous digit
        sb      _PrevIsSpace
        jmp     ADD100k_TstDp

        setb    OutPut+1.4
        clrb    _PrevIsSpace

ENDIF

ADD100k_TstDp:

        ; test for dp
        decsz   DpPlacement     ; test for dp placement
        jmp     ADD100k_NoDp    ; no dp yet
        ; place dp
        setb    _DpUsed ; dp is used
        mov     W, #DECIMAL_POINT       ;
        mov     OutPut+2, W     ; dp is third digit

        ; restore zero before dp
        snb     _PrevIsSpace
        setb    OutPut+1.4

        jmp     SUB30k          ; continue

ADD100k_NoDp:
        ; no dp copy back number
        mov     W, OutPut+3
        mov     OutPut+2, W

        ; test for leading zeroes
        mov     W, #BYTE_OFFSET
        mov     W, OutPut+2-w
        sb      Z
        clrb    _PrevIsSpace
        sb      Z
        jmp     SUB30k

        sb      _PrevIsSpace
        jmp     SUB30k

        ; this digit is zero, and dp is not yet set
        ; then downshift to space
        setb    _PrevIsSpace
        clrb    OutPut+2.4


SUB30k:
        ; subtract 30'000 from sample
        mov     W, #3
        add     OutPut+4, W
        mov     W, #LOW30K
        sub     Sample+2, W

        mov     W, #MID30K
        sb      C
        mov     W, #MID30K + 1
        sub     Sample+1, W

        sb      C
        dec     Sample

        sb      Sample.7        ; negative ?
        jmp     SUB30k
ADD10k:
        ; add 10'000 to sample

        dec     OutPut+4
        mov     W, #LOW10K
        add     Sample+2, W

        mov     W, #MID10K
        snb     C
        mov     W, #MID10K + 1
        add     Sample+1, W

        snb     C
        inc     Sample

        snb     Sample.7        ; done ?
        jmp     ADD10k

        snb     _DpUsed
        jmp     SUB3k           ; dp is already used continue

        ; test for dp
        decsz   DpPlacement     ; test for dp placement
        jmp     ADD10k_NoDp     ; no dp yet
        ; place dp
        setb    _DpUsed ; dp is used
        mov     W, #DECIMAL_POINT
        mov     OutPut+3, W     ; dp is fourth digit

        ; restore zero before dp
        sb      _PrevIsSpace
        setb    OutPut+2.4


        jmp     SUB3k           ; continue

ADD10k_NoDp:
        ; no dp copy back number
        mov     W, OutPut+4
        mov     OutPut+3, W

        ; test for leading zeroes
        mov     W, #BYTE_OFFSET
        mov     W, OutPut+3-w
        sb      Z
        clrb    _PrevIsSpace
        sb      Z
        jmp     SUB3k

        sb      _PrevIsSpace
        jmp     SUB3k

        ; this digit is zero, and dp is not yet set
        ; then downshift to space
        setb    _PrevIsSpace
        clrb    OutPut+3.4

SUB3k:
        ; subtract 3000 from sample
        mov     W, #3
        add     OutPut+5, W
        mov     W, #LOW3K
        sub     Sample+2, W

        mov     W, #MID3K
        sb      C
        mov     W, #MID3K + 1
        sub     Sample+1, W

        snb     C               ; negative ?
        jmp     SUB3k
ADD1k:
        ; add 1000 to sample
        dec     OutPut+5
        mov     W, #LOW1K
        add     Sample+2, W

        mov     W, #MID1K
        snb     C
        mov     W, #MID1K + 1
        add     Sample+1, W

        sb      C
        jmp     ADD1k

        snb     _DpUsed
        jmp     SUB300_PreLoad  ; dp is already used continue

        ; test for dp
        decsz   DpPlacement     ; test for dp placement
        jmp     ADD1k_NoDp      ; no dp yet
        ; place dp

        setb    _DpUsed ; dp is used
        mov     W, #DECIMAL_POINT
        mov     OutPut+4, W     ; dp is fifth digit

        ; restore zero before dp
        snb     _PrevIsSpace
        setb    OutPut+3.4

        jmp     SUB300_PreLoad  ; continue

ADD1k_NoDp:
        ; no dp copy back number
        mov     W, OutPut+5
        mov     OutPut+4, W

        ; test for leading zeroes
        mov     W, #BYTE_OFFSET
        mov     W, OutPut+4-w
        sb      Z
        clrb    _PrevIsSpace
        sb      Z
        jmp     SUB300_PreLoad

        sb      _PrevIsSpace
        jmp     SUB300_PreLoad

        ; this digit is zero, and dp is not yet set
        ; then downshift to space
        setb    _PrevIsSpace
        clrb    OutPut+4.4

SUB300_PreLoad
        mov     W, #BYTE_OFFSET
        mov     OutPut+6, W
SUB300:
        ; subtract 300 from sample
        mov     W, #3
        add     OutPut+6, W
        mov     W, #LOW300
        sub     Sample+2, W

        mov     W, #MID300
        sb      C
        mov     W, #MID300 + 1
        sub     Sample+1, W

        snb     C               ; negative ?
        jmp     SUB300

        mov     W, #100

ADD100:
        ; add 100 to sample
        dec     OutPut+6
        add     Sample+2, W
        sb      C
        jmp     ADD100
        inc     Sample+1
        snb     Sample+1.7
        jmp     ADD100

        snb     _DpUsed
        jmp     SUB30_PreLoad   ; dp is already used continue

        ; test for dp
        decsz   DpPlacement     ; test for dp placement
        jmp     ADD100_NoDp     ; no dp yet
        ; place dp
        setb    _DpUsed         ; dp is used
        mov     W, #DECIMAL_POINT       ;
        mov     OutPut+5, W     ; dp is sixth digit

        ; restore zero before dp
        snb     _PrevIsSpace
        setb    OutPut+4.4

        jmp     SUB30_PreLoad   ; continue

ADD100_NoDp:
        ; no dp copy back number
        mov     W, OutPut+6
        mov     OutPut+5, W

        ; test for leading zeroes
        mov     W, #BYTE_OFFSET
        mov     W, OutPut+5-w
        sb      Z
        clrb    _PrevIsSpace
        sb      Z
        jmp     SUB30_PreLoad

        sb      _PrevIsSpace
        jmp     SUB30_PreLoad

        ; this digit is zero, and dp is not yet set
        ; then downshift to space
        setb    _PrevIsSpace
        clrb    OutPut+5.4


SUB30_PreLoad:
        mov     W, #30
SUB30:
        ; subtract 30 from sample
        inc     OutPut+7
        sub     Sample+2, W
        snb     C
        jmp     SUB30
        mov     W, OutPut+7
        rl      OutPut+7
        add     OutPut+7, W
        mov     W, #10

ADD10:
        ; add 10 to sample
        dec     OutPut+7
        add     Sample+2, W
        sb      C
        jmp     ADD10

        mov     W, #BYTE_OFFSET
        add     OutPut+7, W

        snb     _DpUsed
        jmp     LAST_DIGIT      ; dp is already used continue

        ; test for dp
        decsz   DpPlacement     ; test for dp placement
        jmp     ADD10_NoDp      ; no dp yet
        ; place dp
        setb    _DpUsed ; dp is used
        mov     W, #DECIMAL_POINT
        mov     OutPut+6, W     ; dp is seventh digit

        ; restore zero before dp
        snb     _PrevIsSpace
        setb    OutPut+5.4

        jmp     LAST_DIGIT      ; continue

ADD10_NoDp
        ; no dp copy back number
        mov     W, OutPut+7
        mov     OutPut+6, W
        ; test for leading zeroes
        mov     W, #BYTE_OFFSET
        mov     W, OutPut+6-w
        sb      Z
        jmp     LAST_DIGIT

        sb      _PrevIsSpace
        jmp     LAST_DIGIT

        ; this digit is zero, and dp is not yet set
        ; then downshift to space
        setb    _PrevIsSpace
        clrb    OutPut+6.4

LAST_DIGIT:

        mov     W, #BYTE_OFFSET
        add     W, Sample+2
        mov     Sample+2, W

        sb      _DpUsed
        mov     OutPut+7, W     ; save in previous byte
        snb     _DpUsed
        jmp     END_CONV

        decsz   DpPlacement     ; test for dp placement
        jmp     CLEAR_LAST      ; no dp used at all ??
        mov     W, #DECIMAL_POINT       ;
        mov     OutPut+7, W     ; dp is eigth digit

        ; restore zero before dp
        snb     _PrevIsSpace
        setb    OutPut+6.4

        jmp     END_CONV
CLEAR_LAST:
        ; no dp used copy back number, and clear last digit
        sb      _DpUsed
        clr     OutPut+8        ; clear last digit no dp used

END_CONV
        nop     ; done :-)
        jmp     Start