	NOLIST
;	This file is part of picide, ATA(PI) interface to PIC18 family MCUs.
;	Copyright (C) 2004-5 Toby Thain, toby@telegraphics.com.au

;	This program is free software; you can redistribute it and/or modify
;	it under the terms of the GNU General Public License as published by  
;	the Free Software Foundation; either version 2 of the License, or
;	(at your option) any later version.

;	This program is distributed in the hope that it will be useful,
;	but WITHOUT ANY WARRANTY; without even the implied warranty of
;	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;	GNU General Public License for more details.

;	You should have received a copy of the GNU General Public License  
;	along with this program; if not, write to the Free Software
;	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
	LIST

	TITLE picide
	SUBTITLE "Utility routines"

;; Version history
;; 21-Oct-2004: 0.1 - started by Toby
;; 23-Feb-2005: 1.0 - public release under GPL

; contents of this module:
	global udiv8_8		; unsigned divide 8 bits by 8 bits
	global udiv16_8		; unsigned divide 16 bits by 8 bits
	global udiv16_16	; unsigned divide 16 bits by 16 bits
	global udiv32_16	; unsigned divide 32 bits by 16 bits
	  global divd,divs	; dividend & divisor, inputs to above routines
	global hexdigit		; print W reg as ASCII hex digit
	global puthex		; print W as two hex digits
	global putdec		; print W as decimal digits
	global putdec16		; print 16 bit value in dec16 as decimal
	  global dec16		; input to above routine
	  global decstr		; scratch space for above routine
	global dumpsec		; print sector buffer in hex/ASCII, # lines in W
	global dumpstuff	; dump memory from FSR1 in hex/ASCII, # lines in W
	global rnd8		; quick and dirty LFSR (semi-random numbers)
	  global LFSRVALUEL	; LFSR state

#define UTIL_ASM
#include "defs.inc"

#define DUMPCOLS 16		; controls format of hex dump routines

	extern putch

	udata_acs
divd	res 4	; dividend: 32 bits
divs	res 2	; divisor: 16 bits
dtemp	res 2
divcnt	res 1

temp	res 1
rnd	res 1
rndcnt	res 1
decstr	res 8
dec16	res 2	; value used by putdec16
digcnt	res 1
rowcnt	res 1 ; used by dumpsec
colcnt	res 2
dumpad	res 2
ascii	res DUMPCOLS+1
LFSRVALUEL	res 1	; used by rnd8
psltemp	res 1	; used by putstrbits,putstrlist

	code

putdec:	movwf divd
	lfsr 0,decstr+7
	clrf digcnt
decdig:	  movlw 10
	  rcall udiv8_8
	  movff divd+1,POSTDEC0	; remainder - store digits backwards
	  incf digcnt
	  tstfsz divd	; quotient
	    bra decdig		; no more digits
	; output digits forwards
pdig:	  movf PREINC0,w	
	  addlw '0'
	  call putch
	  decfsz digcnt
	  bra pdig
	return

putdec16:
	lfsr 0,decstr+7
	; set divisor 
	movlw 10
	movwf divs
	clrf divs+1
	; set initial dividend
	movff dec16,divd
	movff dec16+1,divd+1
	clrf digcnt
decdigb:  rcall udiv16_16
	  movff divd+2,POSTDEC0	; remainder - store digits backwards
	  incf digcnt
	  ; quotient becomes next dividend
	  tstfsz divd
	    bra decdigb
	  tstfsz divd+1
	    bra decdigb
	; output digits forwards
pdigb:	  movf PREINC0,w	
	  addlw '0'
	  call putch
	  decfsz digcnt
	    bra pdig
	return

puthex:	movwf temp
	swapf temp,w
	rcall hexdigit
	movf temp,w
hexdigit:
	andlw 0x0f
	addlw -10
	bn isdec
	  addlw 'A'-'0'-10
isdec:	addlw '0'+10
	goto putch

	; print a hex/ASCII dump of the sector buffer
dumpsec: ; # lines is in Wreg
	lfsr 1,secbuf	; set up read pointer
dumpstuff:	; alternative entry with FSR1 pointing at data
	movwf rowcnt
	clrf dumpad	; zero offset
	clrf dumpad+1
dorow:	; print hex offset at start of each line
	movf dumpad+1,w
	rcall puthex
	movf dumpad,w
	rcall puthex
	_putchar ':'
	lfsr 2, ascii	; set up write pointer
	movlw DUMPCOLS	; # columns (bytes per line)
	movwf colcnt
	addwf dumpad	; bump offset by bytes per line
	bnc docol
	  incf dumpad+1
docol:	  movlw 0x7e
	  cpfsgt INDF1
	    bra sevenb ; it's 7-bit, and not DEL
nonprint:   movlw '.'
	    movwf POSTINC2
	    bra dohex
sevenb:	  movlw 0x1f
	  cpfsgt INDF1
	    bra nonprint ; it's a control char
	  movff INDF1,POSTINC2 ; it's ASCII, copy it over
dohex:	  movf POSTINC1,w
	  rcall puthex
	  decfsz colcnt
	    bra docol
	_putchar ' '
	; print ASCII row
	clrf INDF2	; add terminating NUL
	_putstrd ascii
	_crlf
	decfsz rowcnt
	  bra dorow
	return

	; helper routines used by macros in defs.inc
	global putstrtail,putstrlist,putstrbits,psbit,putstrpc,psltemp

	; print NUL-terminated string at TBLPTR
putstrtail:
putit:	  tblrd *+
	  movf TABLAT,w
	  bz donestr
	    call putch
	    bra putit
donestr:return

	; print NUL-terminated string at PC
putstrpc:
	movff TOSU,TBLPTRU
	movff TOSH,TBLPTRH
	movff TOSL,TBLPTRL
putstr2:rcall putstrtail
	; adjust return address to after end of string
fixuppc:btfsc TBLPTRL,0	; is ptr odd?
	  tblrd *+	; yes, bump it by one
	movf TBLPTRU,w	; copy to return PC
	movwf TOSU	; ** cannot use movff **
	movf TBLPTRH,w
	movwf TOSH
	movf TBLPTRL,w
	movwf TOSL
	return

	; macro helper
	; find indexed string in NUL-terminated list (at PC), print it
putstrlist:
	movff TOSU,TBLPTRU
	movff TOSH,TBLPTRH
	movff TOSL,TBLPTRL
	addlw 0	; set flags on W
	bz putstr2
skip:	    tblrd *+
	    movf TABLAT,f
	    bnz skip
	  decfsz WREG
	    bra skip
print:	bra putstr2	; print the string pointed at

  IF 0	; old version
putstrlist:
	movf psltemp,w
isthis:	  bz print
skip:	    tblrd *+
	    movf TABLAT,f
	    bnz skip
	  addlw -1
	  bra isthis
print:	bra putstrtail	; print the string pointed at
  ENDIF

	; macro helper
	; print strings corresponding to bit vector, msb first
putstrbits:
	movwf psltemp
	movff TOSU,TBLPTRU
	movff TOSH,TBLPTRH
	movff TOSL,TBLPTRL
	call psbit
	bra fixuppc

psbit:	  bcf STATUS,C
	  rlcf psltemp,f
	  bc printit	; high bit was set
	  btfsc STATUS,Z	; nothing left?
	    return
	; skip to next string
skipbit:    tblrd *+
	    movf TABLAT,f
	    bnz skipbit
	  bra psbit
printit:  rcall putstrtail
	  bra psbit

  IF 0	; old version
putstrbits:
	movf psltemp,w
nextbit:bcf STATUS,C
	rlcf psltemp
	bc printit	; high bit was set
	btfsc STATUS,Z	; nothing left to print
	return
	; skip to next string
skipbit:    tblrd *+
	    movf TABLAT,f
	    bnz skipbit
	bra nextbit
printit:rcall putstrtail
	bra nextbit
  ENDIF

	; Galois LFSR
	; from: http://www.piclist.com/techref/microchip/rand8bit.htm
	; more info: http://www.shmoo.com/crypto/Cracking_DES/CH10/main.html
	; seed LFSRVALUEL with a non zero value
rnd8:	BCF     STATUS,C    
	RRCF    LFSRVALUEL,W
	BTFSC   STATUS,C
	XORLW   0xB4	; nice trick, sets the incoming MSB according to carry
	MOVWF   LFSRVALUEL
	return

; integer division
; based on code in Nova Programmer's Reference, p E-4
; on entry: divisor in W
;   dividend in divdhi,divdlo (16 bits)
; on exit: quotient in divdlo, remainder in divdhi
;   carry set if overflow
udiv8_8:
	clrf divd+1	; dividend is 8 bits only
udiv16_8:
	movwf divs
	subwf divd+1,w	; dividend hi < divisor?
	bc oflow	; no - result won't fit in 8 bits
	movlw 8		; width of divisor
	movwf divcnt
	; carry is clear here
	rlcf divd
dloop:	  rlcf divd+1
	  movf divs,w
	  subwf divd+1,w
	  ; carry is set if result zero or positive
	  bnc next
	    movwf divd+1	; store subtracted result
next:	  rlcf divd
	  decfsz divcnt
	    bra dloop
oflow:	return

udiv16_16:	; 16 bit dividend only
	clrf divd+2
	clrf divd+3
	; divide 32 bit value by 16 bit value
	; 16 bit quotient in divd1,0
	; 16 bit remainder in divd2,3
udiv32_16:	; OVERFLOW IS NOT CHECKED
	movlw 16	; width of divisor
	movwf divcnt
	bcf STATUS,C	; clear carry here
	rlcf divd
	rlcf divd+1
loopb:	  rlcf divd+2
	  rlcf divd+3
	  ; trial subtraction - divisor from high word of dividend
	  movff divd+2,dtemp
	  movf divs,w
	  subwf dtemp
	  movff divd+3,dtemp+1
	  movf divs+1,w
	  subwfb dtemp+1
	  ; carry is set if result zero or positive
	  bnc nextb
	    movff dtemp,divd+2	; store subtracted result
	    movff dtemp+1,divd+3
nextb:	  rlcf divd
	  rlcf divd+1
	  decfsz divcnt
	    bra loopb
	return

	end
