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.
;**********************************************************************
; This file is a basic code template for assembly code generation *
; on the PICmicro PIC16F877. This file contains the basic code *
; building blocks to build upon. *
; *
; If interrupts are not used all code presented between the ORG *
; 0x004 directive and the label main can be removed. In addition *
; the variable assignments for 'w_temp' and 'status_temp' can *
; be removed. *
; *
; Refer to the MPASM User's Guide for additional information on *
; features of the assembler (Document DS33014). *
; *
; Refer to the respective PICmicro data sheet for additional *
; information on the instruction set. *
; *
; Template file assembled with MPLAB V4.00 and MPASM V2.20.00. *
; *
;**********************************************************************
; *
; Filename: MathTest.asm 24bit signed binary->ASCII w. dp *
; Date: 2000-03-31 *
; File Version: 0.1B *
; *
; Author: Tony Kübek *
; Company: *
; *
; *
;**********************************************************************
; *
; Files required: *
; *
; *
; *
;**********************************************************************
; *
; 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 *
;**********************************************************************
list p=16f877 ; list directive to define processor
#include <p16f877.inc> ; processor specific variable definitions
__CONFIG _CP_OFF & _WDT_ON & _BODEN_ON & _PWRTE_ON & _RC_OSC & _WRT_ENABLE_ON & _LVP_OFF& _DEBUG_OFF & _CPD_OFF
; '__CONFIG' directive is used to embed configuration data within .asm file.
; The lables following the directive are located in the respective .inc file.
; See respective data sheet for additional information on configuration word.
#define TEST_SMALL_NUMBER ; 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
#define DECIMAL_POINT 0x2E ; '.'
#define BYTE_OFFSET 0x30 ; start decimal ASCII numbers
#define SIGN_NEGATIVE 0x2D ; '-'
;***** VARIABLE DEFINITIONS
CBLOCK 0x20 ; start userram bank 0
BitVars:1 ; used for dp-bit variable
OutPut:9 ; 8 used if no dp, else 9
DpPlacement: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
ENDC
w_temp EQU 0x70 ; variable used for context saving
status_temp EQU 0x71 ; variable used for context saving
#define _DpUsed BitVars,0 ; decimal point has been placed
#define _PrevIsSpace BitVars,1 ; previous byte/digit is 'converted' to space
#define Sample OutPut+6 ; Note !! to save ram, 24 bit variable is stored
; in highest three byte in 9 byte string
;**********************************************************************
ORG 0x000 ; processor reset vector
clrf PCLATH ; ensure page bits are cleared
goto MAIN ; go to beginning of program
ORG 0x004 ; interrupt vector location
movwf w_temp ; save off current W register contents
movf STATUS,w ; move status register into W register
movwf status_temp ; save off contents of STATUS register
; isr code can go here or be located as a call subroutine elsewhere
movf status_temp,w ; retrieve copy of STATUS register
movwf STATUS ; restore pre-isr STATUS register contents
swapf w_temp,f
swapf w_temp,w ; restore pre-isr W register contents
retfie ; return from interrupt
MAIN
;************************ 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!
;************************************
CLRF BitVars
; dp=1->dp=7(no dp), adds approx 50 cycles, i.e. dp=1 least cycles
MOVLW 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
MOVWF DpPlacement
MOVLW BYTE_OFFSET ; preset with ASCII offset
MOVWF OutPut
MOVWF OutPut+1
MOVWF OutPut+2
MOVWF OutPut+3
MOVWF OutPut+4
MOVWF OutPut+5
BCF 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
; MOVLW 0x47
; MOVWF Sample+2
; MOVLW 0xDB
; MOVWF Sample+1
; MOVLW 0x12
; MOVWF Sample
; test number : -1235783 = 0xED24B9 - ok ! cycles approx: 311 -360 depending on dp placment
; Note ! This with TEST_SMALL_NUMBER, without deduct 10 cycles
; MOVLW 0xB9
; MOVWF Sample+2
; MOVLW 0x24
; MOVWF Sample+1
; MOVLW 0xED
; MOVWF Sample
; test number : 60900 = 0x00EDE4 - ok ! cycles approx: 315 -371 depending on dp placment
; Note ! This with TEST_SMALL_NUMBER, without add 63 cycles
; MOVLW 0xE4
; MOVWF Sample+2
; MOVLW 0xED
; MOVWF Sample+1
; MOVLW 0x00
; MOVWF Sample
; test number : -60900 = 0xFF121C - ok ! cycles approx: 325 -381 depending on dp placment
; Note ! This with TEST_SMALL_NUMBER, without add 56 cycles
; MOVLW 0x1C
; MOVWF Sample+2
; MOVLW 0x12
; MOVWF Sample+1
; MOVLW 0xFF
; MOVWF Sample
; test number : 231 = 0xE7 - ok !, cycles approx 244-302 depending on dp placement
; NOTE ! With TEST_SMALL_NUMBER, without add 62 cycles
; MOVLW 0xE7
; MOVWF Sample+2
; MOVLW 0x00
; MOVWF Sample+1
; MOVLW 0x00
; MOVWF Sample
; test number : -1 = 0xFFFFFF - ok !, cycles approx:262-326 depending on dp placement
; This with TEST_SMALL_NUMBER, without add 62 cycles
;
; MOVLW 0xFF
; MOVWF Sample+2
; MOVLW 0xFF
; MOVWF Sample+1
; MOVLW 0xFF
; MOVWF Sample
; test number : 1048575 = 0x0FFFFF - ok !, cycles approx: 297-348 depending on dp placement
; this with TEST_SMALL_NUMBER, without add 8 cycles
; MOVLW 0xFF
; MOVWF Sample+2
; MOVLW 0xFF
; MOVWF Sample+1
; MOVLW 0x0F
; MOVWF Sample
; test number : 1000000 = 0x0F4240 - ok !, cycles approx: 291-350 depending on dp placement
; this with TEST_SMALL_NUMBER, without add 8 cycles
; MOVLW 0x40
; MOVWF Sample+2
; MOVLW 0x42
; MOVWF Sample+1
; MOVLW 0x0F
; MOVWF Sample
; test number : 8388607 = 0x7FFFFF - ok !, cycles approx: 394-445 depending on dp placement
; this with TEST_SMALL_NUMBER, without add 8 cycles
; MOVLW 0xFF
; MOVWF Sample+2
; MOVLW 0xFF
; MOVWF Sample+1
; MOVLW 0x7F
; MOVWF Sample
; test number : -7099999 = 0x93A9A1 - ok !, cycles approx:429-480 depending on dp placement
; this with TEST_SMALL_NUMBER, without add 8 cycles
MOVLW 0xA1
MOVWF Sample+2
MOVLW 0xA9
MOVWF Sample+1
MOVLW 0x93
MOVWF Sample
TEST_NEGATIVE
BTFSS Sample,7 ; test if negative
IFDEF TEST_SMALL_NUMBER
GOTO TEST_TOPNIBBLE ; nope
ELSE
GOTO SUB4M ; nope
ENDIF
; sample is negative
; make two's complement
COMF Sample+2,F
COMF Sample+1,F
COMF Sample,F
MOVLW 1
ADDWF Sample+2,F
SKPNC
ADDWF Sample+1,F
SKPNC
ADDWF Sample,F
MOVLW SIGN_NEGATIVE
MOVWF OutPut ; first digit minus sign
IFDEF TEST_SMALL_NUMBER ; 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 1048575 ( decimal )
; if so we can skip to sub300k routine
MOVF Sample,w
ANDLW 0x70 ; Mask out top three bits
BNZ SUB4M ; nope do full conversion
; this digit is zero, and dp is not yet set
; then downshift to space
BSF _PrevIsSpace
BCF OutPut+1,4
GOTO SUB300k ; continue
ENDIF ; TEST_SMALL_NUMBER
SUB4M:
; subtract 4 million from sample
MOVLW 4
ADDWF OutPut+1, f
MOVLW low(4000000)
SUBWF Sample+2, f
MOVLW low(4000000>>8)
SKPC
MOVLW low(4000000>>8)+1
SUBWF Sample+1, f
MOVLW low(4000000>>16)
SKPC
MOVLW low(4000000>>16) + 1
SUBWF Sample, f
SKPNC ; overflow, i.e. negative
GOTO SUB4M
ADD1M:
; add 1 million to sample
DECF OutPut+1, f
MOVLW low(1000000)
ADDWF Sample+2, f
MOVLW low(1000000>>8)
SKPNC
MOVLW low(1000000>>8) + 1
ADDWF Sample+1, f
MOVLW low(1000000>>16)
SKPNC
MOVLW low(1000000>>16) + 1
ADDWF Sample, f
SKPC ; done ?
GOTO ADD1M
; test for leading zeroes
MOVLW BYTE_OFFSET
SUBWF OutPut+1,w
BNZ SUB300k
; this digit is zero, and dp is not yet set
; then downshift to space
BSF _PrevIsSpace
BCF OutPut+1,4
SUB300k:
; substract 300 thousand from sample
MOVLW 3
ADDWF OutPut+3, f
MOVLW low(300000)
SUBWF Sample+2, f
MOVLW low(300000>>8)
SKPC
MOVLW low(300000>>8)+1
SUBWF Sample+1, f
MOVLW low(300000>>16)
SKPC
MOVLW low(300000>>16) + 1
SUBWF Sample, f
SKPNC
GOTO SUB300k
ADD100k
; add 100 thousand to sample
DECF OutPut+3, f
MOVLW low(100000)
ADDWF Sample+2, f
MOVLW low(100000>>8)
SKPNC
MOVLW low(100000>>8) + 1
ADDWF Sample+1, f
MOVLW low(100000>>16)
SKPNC
MOVLW low(100000>>16) + 1
ADDWF Sample, f
SKPC ; done
GOTO ADD100k
IFDEF TEST_SMALL_NUMBER ; if this test enabled it is possible
; to have an overflow here
; test for overflow ( Output+3 = 10 )
MOVLW BYTE_OFFSET+0x0A
SUBWF OutPut+3,W
BTFSS STATUS,Z ; test if non zero
GOTO ADD100k_TstDp
; over flow, result is = 10, second digit = 1, third = 0
MOVLW BYTE_OFFSET
MOVWF OutPut+3
BTFSS _DpUsed ; test if dp already used
BSF OutPut+1,0 ; if no dp used set this digit to one
BSF OutPut+2,0 ; set also third digit to 1 ( in case dp is used )
; restore previous digit
BTFSS _PrevIsSpace
GOTO ADD100k_TstDp
BSF OutPut+1,4
BCF _PrevIsSpace
ENDIF
ADD100k_TstDp:
; test for dp
DECFSZ DpPlacement,F ; test for dp placement
GOTO ADD100k_NoDp ; no dp yet
; place dp
BSF _DpUsed ; dp is used
MOVLW DECIMAL_POINT ;
MOVWF OutPut+2 ; dp is third digit
; restore zero before dp
BTFSC _PrevIsSpace
BSF OutPut+1,4
GOTO SUB30k ; continue
ADD100k_NoDp:
; no dp copy back number
MOVF OutPut+3,W
MOVWF OutPut+2
; test for leading zeroes
MOVLW BYTE_OFFSET
SUBWF OutPut+2,w
BTFSS STATUS,Z
BCF _PrevIsSpace
BNZ SUB30k
BTFSS _PrevIsSpace
GOTO SUB30k
; this digit is zero, and dp is not yet set
; then downshift to space
BSF _PrevIsSpace
BCF OutPut+2,4
SUB30k:
; subtract 30'000 from sample
MOVLW 3
ADDWF OutPut+4, f
MOVLW low(30000)
SUBWF Sample+2, f
MOVLW low(30000>>8)
SKPC
MOVLW low(30000>>8)+1
SUBWF Sample+1, f
MOVLW low(30000>>16)
SKPC
MOVLW low(30000>>16) + 1
SUBWF Sample, f
SKPNC ; negative ?
GOTO SUB30k
ADD10k:
; add 10'000 to sample
DECF OutPut+4, f
MOVLW low(10000)
ADDWF Sample+2, f
MOVLW low(10000>>8)
SKPNC
MOVLW low(10000>>8) + 1
ADDWF Sample+1, f
MOVLW low(10000>>16)
SKPNC
MOVLW low(10000>>16) + 1
ADDWF Sample, f
SKPC ; done ?
GOTO ADD10k
BTFSC _DpUsed
GOTO SUB3k ; dp is already used continue
; test for dp
DECFSZ DpPlacement,F ; test for dp placement
GOTO ADD10k_NoDp ; no dp yet
; place dp
BSF _DpUsed ; dp is used
MOVLW DECIMAL_POINT ;
MOVWF OutPut+3 ; dp is fourth digit
; restore zero before dp
BTFSS _PrevIsSpace
BSF OutPut+2,4
GOTO SUB3k ; continue
ADD10k_NoDp:
; no dp copy back number
MOVF OutPut+4,W
MOVWF OutPut+3
; test for leading zeroes
MOVLW BYTE_OFFSET
SUBWF OutPut+3,w
BTFSS STATUS,Z
BCF _PrevIsSpace
BNZ SUB3k
BTFSS _PrevIsSpace
GOTO SUB3k
; this digit is zero, and dp is not yet set
; then downshift to space
BSF _PrevIsSpace
BCF OutPut+3,4
SUB3k:
; subtract 3000 from sample
MOVLW 3
ADDWF OutPut+5, f
MOVLW low(3000)
SUBWF Sample+2, f
MOVLW low(3000>>8)
SKPC
MOVLW low(3000>>8)+1
SUBWF Sample+1, f
SKPNC ; negative ?
GOTO SUB3k
ADD1k:
; add 1000 to sample
DECF OutPut+5, f
MOVLW low(1000)
ADDWF Sample+2, f
MOVLW low(1000>>8)
SKPNC
MOVLW low(1000>>8) + 1
ADDWF Sample+1, f
SKPC
GOTO ADD1k
BTFSC _DpUsed
GOTO SUB300_PreLoad ; dp is already used continue
; test for dp
DECFSZ DpPlacement,F ; test for dp placement
GOTO ADD1k_NoDp ; no dp yet
; place dp
BSF _DpUsed ; dp is used
MOVLW DECIMAL_POINT ;
MOVWF OutPut+4 ; dp is fifth digit
; restore zero before dp
BTFSC _PrevIsSpace
BSF OutPut+3,4
GOTO SUB300_PreLoad ; continue
ADD1k_NoDp:
; no dp copy back number
MOVF OutPut+5,W
MOVWF OutPut+4
; test for leading zeroes
MOVLW BYTE_OFFSET
SUBWF OutPut+4,w
BTFSS STATUS,Z
BCF _PrevIsSpace
BNZ SUB300_PreLoad
BTFSS _PrevIsSpace
GOTO SUB300_PreLoad
; this digit is zero, and dp is not yet set
; then downshift to space
BSF _PrevIsSpace
BCF OutPut+4,4
SUB300_PreLoad
MOVLW BYTE_OFFSET
MOVWF OutPut+6
SUB300:
; subtract 300 from sample
MOVLW 3
ADDWF OutPut+6, f
MOVLW low(300)
SUBWF Sample+2, f
MOVLW low(300>>8)
SKPC
MOVLW low(300>>8)+1
SUBWF Sample+1, f
SKPNC ; negative ?
GOTO SUB300
MOVLW 100
ADD100:
; add 100 to sample
DECF OutPut+6, f
ADDWF Sample+2, f
SKPC
GOTO ADD100
INCF Sample+1, f
BTFSC Sample+1, 7
GOTO ADD100
BTFSC _DpUsed
GOTO SUB30_PreLoad ; dp is already used continue
; test for dp
DECFSZ DpPlacement,F ; test for dp placement
GOTO ADD100_NoDp ; no dp yet
; place dp
BSF _DpUsed ; dp is used
MOVLW DECIMAL_POINT ;
MOVWF OutPut+5 ; dp is sixth digit
; restore zero before dp
BTFSC _PrevIsSpace
BSF OutPut+4,4
GOTO SUB30_PreLoad ; continue
ADD100_NoDp:
; no dp copy back number
MOVF OutPut+6,W
MOVWF OutPut+5
; test for leading zeroes
MOVLW BYTE_OFFSET
SUBWF OutPut+5,w
BTFSS STATUS,Z
BCF _PrevIsSpace
BNZ SUB30_PreLoad
BTFSS _PrevIsSpace
GOTO SUB30_PreLoad
; this digit is zero, and dp is not yet set
; then downshift to space
BSF _PrevIsSpace
BCF OutPut+5,4
SUB30_PreLoad:
MOVLW 30
SUB30:
; subtract 30 from sample
INCF OutPut+7, f
SUBWF Sample+2, f
SKPNC
GOTO SUB30
MOVFW OutPut+7
RLF OutPut+7, f
ADDWF OutPut+7, f
MOVLW 10
ADD10:
; add 10 to sample
DECF OutPut+7, f
ADDWF Sample+2, f
SKPC
GOTO ADD10
MOVLW BYTE_OFFSET
ADDWF OutPut+7,f
BTFSC _DpUsed
GOTO LAST_DIGIT ; dp is already used continue
; test for dp
DECFSZ DpPlacement,F ; test for dp placement
GOTO ADD10_NoDp ; no dp yet
; place dp
BSF _DpUsed ; dp is used
MOVLW DECIMAL_POINT ;
MOVWF OutPut+6 ; dp is seventh digit
; restore zero before dp
BTFSC _PrevIsSpace
BSF OutPut+5,4
GOTO LAST_DIGIT ; continue
ADD10_NoDp
; no dp copy back number
MOVF OutPut+7,W
MOVWF OutPut+6
; test for leading zeroes
MOVLW BYTE_OFFSET
SUBWF OutPut+6,w
BNZ LAST_DIGIT
BTFSS _PrevIsSpace
GOTO LAST_DIGIT
; this digit is zero, and dp is not yet set
; then downshift to space
BSF _PrevIsSpace
BCF OutPut+6,4
LAST_DIGIT:
MOVLW BYTE_OFFSET
ADDWF Sample+2,w
MOVWF Sample+2
BTFSS _DpUsed
MOVWF OutPut+7 ; save in previous byte
BTFSC _DpUsed
GOTO END_CONV
DECFSZ DpPlacement,F ; test for dp placement
GOTO CLEAR_LAST ; no dp used at all ??
MOVLW DECIMAL_POINT ;
MOVWF OutPut+7 ; dp is eigth digit
; restore zero before dp
BTFSC _PrevIsSpace
BSF OutPut+6,4
GOTO END_CONV
CLEAR_LAST:
; no dp used copy back number, and clear last digit
BTFSS _DpUsed
CLRF OutPut+8 ; clear last digit no dp used
END_CONV
NOP ; done :-)
GOTO MAIN
END ; directive 'end of program'