PIC Microcontoller 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.
;**********************************************************************
;   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	$70	; variable used for context saving
status_temp	equ	$71	; 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	$000	; processor reset vector
;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
;		clrf    PCLATH            ; ensure page bits are cleared
	clr	PCLATH	; ensure page bits are cleared
	jmp	MAIN	; go to beginning of program


	org	$004	; interrupt vector location
	mov	w_temp, W	; save off current W register contents
	mov	W, STATUS	; move status register into W register
	mov	status_temp, W	; save off contents of STATUS register


; isr code can go here or be located as a call subroutine elsewhere


	mov	W, status_temp	; retrieve copy of STATUS register
	mov	STATUS, W	; restore pre-isr STATUS register contents
	swap	w_temp
	mov	W, <>w_temp	; restore pre-isr W register contents
;*** WARNING: SX saves/restores W, STATUS, and FSR automatically.
;		retfie                    ; return from interrupt
	reti	; 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!
;************************************
	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
	IFDEF	TEST_SMALL_NUMBER
	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



	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

	mov	W, Sample
	and	W, #$70	; Mask out top three bits
;*** WARNING: MPASM macro BNZ is not supported yet. Replace manually.
	BNZ	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, #low(4000000)
	sub	Sample+2, W

	mov	W, #low(4000000>	;>8)
	sb	C
	mov	W, #low(4000000>	;>8)+1
	sub	Sample+1, W

	mov	W, #low(4000000>	;>16)
	sb	C
	mov	W, #low(4000000>	;>16) + 1
	sub	Sample, W


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

ADD1M:
        ; add 1 million to sample
	dec	OutPut+1
	mov	W, #low(1000000)
	add	Sample+2, W

	mov	W, #low(1000000>	;>8)
	snb	C
	mov	W, #low(1000000>	;>8) + 1
	add	Sample+1, W

	mov	W, #low(1000000>	;>16)
	snb	C
	mov	W, #low(1000000>	;>16) + 1
	add	Sample, W

	sb	C	; done ?
	jmp	ADD1M

	; test for leading zeroes
	mov	W, #BYTE_OFFSET
	mov	W, OutPut+1-w
;*** WARNING: MPASM macro BNZ is not supported yet. Replace manually.
	BNZ	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, #low(300000)
	sub	Sample+2, W

	mov	W, #low(300000>	;>8)
	sb	C
	mov	W, #low(300000>	;>8)+1
	sub	Sample+1, W

	mov	W, #low(300000>	;>16)
	sb	C
	mov	W, #low(300000>	;>16) + 1
	sub	Sample, W


	snb	C
	jmp	SUB300k

ADD100k
        ; add 100 thousand to sample
	dec	OutPut+3
	mov	W, #low(100000)
	add	Sample+2, W

	mov	W, #low(100000>	;>8)
	snb	C
	mov	W, #low(100000>	;>8) + 1
	add	Sample+1, W

	mov	W, #low(100000>	;>16)
	snb	C
	mov	W, #low(100000>	;>16) + 1
	add	Sample, W

	sb	C	; done
	jmp	ADD100k
	IFDEF	TEST_SMALL_NUMBER ; 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
;*** WARNING: MPASM macro BNZ is not supported yet. Replace manually.
	BNZ	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, #low(30000)
	sub	Sample+2, W

	mov	W, #low(30000>	;>8)
	sb	C
	mov	W, #low(30000>	;>8)+1
	sub	Sample+1, W

	mov	W, #low(30000>	;>16)
	sb	C
	mov	W, #low(30000>	;>16) + 1
	sub	Sample, W


	snb	C	; negative ?
	jmp	SUB30k
ADD10k:
        ; add 10'000 to sample

	dec	OutPut+4
	mov	W, #low(10000)
	add	Sample+2, W

	mov	W, #low(10000>	;>8)
	snb	C
	mov	W, #low(10000>	;>8) + 1
	add	Sample+1, W

	mov	W, #low(10000>	;>16)
	snb	C
	mov	W, #low(10000>	;>16) + 1
	add	Sample, W

	sb	C	; 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
;*** WARNING: MPASM macro BNZ is not supported yet. Replace manually.
	BNZ	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, #low(3000)
	sub	Sample+2, W

	mov	W, #low(3000>	;>8)
	sb	C
	mov	W, #low(3000>	;>8)+1
	sub	Sample+1, W

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

	mov	W, #low(1000>	;>8)
	snb	C
	mov	W, #low(1000>	;>8) + 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
;*** WARNING: MPASM macro BNZ is not supported yet. Replace manually.
	BNZ	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, #low(300)
	sub	Sample+2, W

	mov	W, #low(300>	;>8)
	sb	C
	mov	W, #low(300>	;>8)+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
;*** WARNING: MPASM macro BNZ is not supported yet. Replace manually.
	BNZ	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
;*** WARNING: MPASM macro BNZ is not supported yet. Replace manually.
	BNZ	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	MAIN




		END                       ; directive 'end of program'