;***************************************************
;*** a Small starter, & runtime lib for the AVRs ***
;*** Non-high level Language dependent	         ***
;*** Works for the ATS902xxx and ATS908xxx chips ***
;*** March 1999 by Jack Tidwell                  ***
;***************************************************
;
;#{_flushRS232
_flushRS232:
	clr  	zl
	sts 	_incount,zl
	sts 	_rxRS232pos,zl
	sts 	_rxRS232pos+1,zl
	lds  	zl,_isrflags
	andi  	zl,0x7f		; clear EOL
	sts  	_isrflags,zl
	ret
;#}
;
;*********************************
;* Send Sram ASCII text Data out *
;*********************************
;#{_txSRncr
_txSRncr:
	rcall 	_txSRn
	rjmp 	_txRS232CR
;#{_txSRn
_txSRn:
	ld  	r24,z+
	rcall	_txRS232
	dec 	r25
	brne	_txSRn
	ret
;********************************************
;* Send a 'null' terminated ASCII message   *
;*  from Sram memory to the RS232 port.     *
;* zl,(r30) has the Sram Address low byte   *
;* zh,(r31) has the Sram Address high byte  *
;********************************************
;#{ _txSRcr
_txSRcr:
	rcall	_txSR
	rjmp	_txRS232CR
;#{ _txSR
_txSR:
	ld        r24,z+
	tst       r24		; test for the 'ending (null) byte
	breq	_txRS232_X	; are we at the end of the message ?
	rcall	_txRS232	; No, send this byte out
	rjmp	_txSR		; get the next character
;********************************************
;* Works in the same manner as the Sram func*
;* Except, the data is 'read' from the Flash*
;* zl,(r30) has the Flash Address low byte  *
;* zh,(r31) has the Flash Address high byte *
;********************************************
;** Send a 'String'/byte(s) from Flash Mem **
;#{_txRS232_SCcr
;#{_txRS232_SC
_txRS232_SCcr:
	rcall	_txRS232_SC
	rjmp	_txRS232CR
_txRS232_SC:
	lpm		        	; load the byte pointed 'to' by the zl & zh regs
	adiw	zl,1		; increment the 'location' address by 1
	mov 	r24,r0		; The AVR 'lpm' puts the data into register 'r0'
	tst 	r24 		; Test the byte for ending NULL.
	breq	_txRS232_X	; Have we reached the end-of-ASCII message ?
	rcall	_txRS232	; No, send it out
	rjmp	_txRS232_SC	; get the next 'character' etc.
_txRS232_X:				; Shared exit for Sram & Flash
	ret
;
;********************************************
;* Check if any characters are waiting to be*
;* processed. return 0 if none, else the    *
;* next character. Then, count = count -1   *
;********************************************
;#{_gettty
;#{_getdig
;#{_rxRS232
;******************************************
;* Convert lower case Alpha to upper case *
;******************************************
;#{_toupper
_toupper:
	cpi 	zl,'a'
	brlo	_toupperx
	cpi 	zl,'z'+1
	brsh	_toupperx
	subi	zl,' '
_toupperx:
	ret
_rxRS232:
	clr 	zl
	lds 	r0,_rxRS232pos
	lds 	zh,_rxRS232pos+1
	cp  	r0,zh
	brne	_rxRS232a
	clr  	zh
	clc  	
	ret
_rxRS232a:
	push	xl
	push	xh
	lds  	r0,_sysflags
	ldi 	xl,low(_rxRS232Buf)
	ldi 	xh,high(_rxRS232Buf)
	add 	xl,zh
	adc 	xh,zl
	ld  	zl,x
	sbrc	r0,3
	rcall	_toupper
	sts  	_prevchar,zl
	cpi  	zl,0x0d  	; Is it a CR ?
	brne  	_rxRS232b	; No, continue
	push 	zl			; Yes, save the current char
	lds  	zl,_isrflags
	cbr  	zl,0x80		; clear the EOL flag
	sts  	_isrflags,zl
	pop  	zl
_rxRS232b:
	inc 	zh
	cpi 	zh,RS232BSIZE
	brlo	_rxRS232x
	clr 	zh
_rxRS232x:
	sts 	_rxRS232pos+1,zh
	cli
	lds 	zh,_incount
	dec 	zh
	sts 	_incount,zh
	sei
	pop 	xh
	pop 	xl
_rxRS232xx:
	clr 	zh
	or  	zl,zl
	sec
	ret
;#}
;********************************************
;* This Interupt Service Routine (ISR) will *
;* 'catch' any new characters comming in    *
;* from the 'host' PC.It's circular, and the*
;* 'buffer' size is adjustable.             *
;********************************************
;#{_isrURXC
_isrURXC:
	push	r1        ; Save user register
	in  	r1,SREG   ; Also, save the current program Status 'flags' register
	push	zl        ; Must save any registers that we might use!
	push	zh
	push	r24
	push	r25
	ldi 	zl,low(_rxRS232Buf)
	ldi 	zh,high(_rxRS232Buf)
	lds 	r24,_rxRS232pos
	clr 	r25
	add 	zl,r24
	adc 	zh,r25
	in  	r25,UDR
	st  	z,r25
	cpi 	r25,0x0d  ; Did the user press 'enter' ?
	brne	_rxUByte1 ; No, don't flag the EOL bit
	lds 	zl,_isrflags	; This flag is 'polled', to indicate EOL
	sbr 	zl,0x80		; _isrflags.7 is the rs232 'EOL' marker
	sts 	_isrflags,zl
_rxUByte1:
	lds 	r25,_incount
	inc 	r25
	sts 	_incount,r25
	inc 	r24		; add 1 byte to buffer count
	cpi 	r24,RS232BSIZE	; Is buffer full? (.equ this to the desired buffer size)
	brlo	_rxUBytex ; No, room for 1 more
	clr 	r24		; Yes, put the next byte at the first location
_rxUBytex:
	sts 	_rxRS232pos,r24	; Update the Index position
	pop 	r25		; restore protected registers
	pop 	r24
	pop 	zh
	pop 	zl
	out 	SREG,r1		; restore Status Register to previous state!
	pop 	r1
	reti			; return from interrupt
;#}
;
;********************************************
;* Write your own LCD functions, 4 or 8 bit *
;********************************************
;#{_LCDRT
.include "lcdrt.asm"
;#}
;
;#{_delayCyc
_delayCyc:
	dec 	zl
	brne 	_delayCyc
	ret
;#}
;
;************************************************
;* Every .001 seconds, the MCU is halted, and   *
;* 'jumps' to this Interupt Service Routine.    *
;* Ideally, ISRs are 'transparent' to the User. *
;************************************************
;#{_isrT0:
_isrT0:
	push	r1		; Save any possible 'in-use' registers
	in  	r1,SREG		; Safe-guard the Status Register
	push	zl
	push	zh
	ldi 	zl,T0_RELOAD	; re-load the 8bit value for the next 'count up'
	out 	TCNT0,ZL
	lds 	zl,_systic	; Let's add 1 to the system 'tic' count
	lds 	zh,_systic+1
	adiw	zl,1
	sts 	_systic,zl
	sts 	_systic+1,zh
	pop 	zh                  ; Now restore everything that was 'disturbed'
	pop 	zl
	out 	SREG,r1
	pop 	r1
	reti                          ; return as though nothing happened!
;#}
;
;********************************************
;* A simple 'wait' for timer 'turnover'	    *
;* In this sys, tmr 0 overflows 1/1000sec   *
;* and increments an Sram memory location   *
;* By counting the 'rollovers', we can get  *
;* a fairly accurate timer, in 1ms steps.   *
;********************************************
;#{ _systic
;#{ _delayMsb
;#{ _delayMs
;#{WaitTic
WaitTic:
	lds 	r24,_systic	; get initial 'tic' low byte value
WaitTiclp:
	lds 	r25,_systic	; wait for the next 'Tic'
	cp  	r24,r25		; is it the same as initial value ?
	breq	WaitTiclp ; Yes, stay in 'loop' until a change occurs.
	ret
;
;*************************************************
;* Delay 'x' number of Milli Seconds (x=r30,r31) *
;* If 'x' < 256, call the _delayMsb. r30 = x     *
;*************************************************
_delayMsb:
	clr 	zh
_delayMs:			; 'x' Milli-second(s) delay
	rcall	WaitTic	; do at least 1 full/partial dly
	clr 	r24	; in case 'x' is zero
	or  	r24,zl
	or  	r24,zh
	breq	_delayMsx
	sbiw	zl,1
	rjmp	_delayMs
_delayMsx:
	ret
;#}
;
;**********************************************
;* Combined 8/16/32 bit signed/unsigned Print *
;* with leading Zero supression               *
;**********************************************
;* print constant tables *
;#{_printcsdecwa
;#{_printcdecwa
_ccalcpoffs:
	lsl 	r24
	rol 	r25
	add  	zl,r24
	adc  	zh,r25
	lpm
	push	r0
	adiw	zl,1
	lpm
	mov 	zh,r0
	pop 	zl
	ret
;
_printcsdecwa:
	rcall	_ccalcoffs
	rjmp 	_printsdecw
_printcdecwa:
	rcall	_ccalcoffs
	rjmp 	_printdecw
;
;#{_printsdecwa
;#{_printdecwa
;#{_printdecba
_calcpoffs:
	lsl 	r24
	rol 	r25
_calcpoffsb:
	mov  	xl,zl
	mov  	xh,zh
	add  	xl,r24
	adc  	xh,r25
	ld   	zl,x+
	ld  	zh,x+
	ret
;
_printsdecwa:
	rcall 	_calcpoffs
	rjmp 	_printsdecw
_printdecwa:
	rcall 	_calcpoffs
	rjmp 	_printdecw
_printdecba:
	rcall 	_calcpoffsb
	rjmp 	_printdecb
;
;#{_printdecb
;#{_printdecw
;#{_printsdecw
;#{_printdecdw
;#{_printsdecdw
_printsdecw:
	sbrs	zh,7		; Is the number < 0 ?
	rjmp	_printdecw	; No, print 0 to 32767
	ser 	r22
	ser 	r23
	rjmp	_printsdecdw
_printdecb:
	clr 	zh
_printdecw:
	clr 	r22
	clr  	r23
_printsdecdw:
	sbrs	r23,7
	rjmp	_printdecdw
	com 	zl
	com 	zh
	com 	r22
	com 	r23
	subi	zl,-1
	sbci	zh,-1
	sbci	r22,-1
	sbci	r23,-1
	ldi 	r24,'-'
	rcall	_putdchar
_printdecdw:
_ltoa:
	push	r1
	push	r2
	push	r3
	push	r4
	push	r5
	push	yl
	push	yh
	clr 	r1
	clr 	r2
	clr 	r3
	clr 	r4
	clr 	r5
	ldi 	r20,32
_ltoa1:
	ldi 	yl,1
	clr  	yh
	ldi 	r21,5
_ltoa2:
	ldi 	r25,51
	ld  	r24,y
	add 	r24,r25
	sbrc 	r24,7
	andi	r25,15
	sbrc	r24,3
	andi	r25,240
	sub 	r24,r25
	st  	y+,r24
	dec 	r21
	brne	_ltoa2
	lsl 	zl
	rol  	zh
	rol 	r22
	rol 	r23
	rol 	r1
	rol 	r2
	rol 	r3
	rol 	r4
	rol 	r5
	dec 	r20
	brne 	_ltoa1
;	rcall	_predp
	clt
	ldi 	zl,6
	clr 	zh
_ltoa3:
	ld  	r24,-z
	rcall 	_prcdwrd
	cpi 	zl,2
	brsh 	_ltoa3
	brts	_ltoa4
	set
	mov 	r24,r1
	rcall	_prcdwrd2
_ltoa4:
	clr 	r24
	sts 	__dp,r24
	sts 	__dp+1,r24
	pop 	yh
	pop 	yl
	pop  	r5
	pop  	r4
	pop  	r3
	pop  	r2
	pop  	r1
	ret
_prcdwrd:
	push 	r24
	swap	r24
	rcall	_prcdwrd2
	pop  	r24
_prcdwrd2:
	andi	r24,0x0f
	subi	r24,-'0'
	rcall	_tstdp
	brts	_prcdwrd4
	cpi 	r24,'0'
	brne	_prcdwrd3
	ret
_prcdwrd3:
	set
_prcdwrd4:
_putdchar:
	rjmp	_txRS232
_tstdp:
	push 	r24
	lds  	r24,__dp
	dec 	r24
	sts 	__dp,r24
	brne 	_tstdpx
	set
	ldi 	r24,'.'
	rcall	_putdchar
_tstdpx:
	pop 	r24
	ret
_predp:
	ret
;#}
;

;*************************
;* Print 2/4/8 Hexdigits *
;*************************
;#{_printhexdw
_printhexdw:
	push	zl
	push	zh
	mov 	zl,r22
	mov 	zh,r23
	rcall	_printhexw
	pop 	zh
	pop 	zl
;#{_printhexw
_printhexw:
	push      zl
    mov       zl,zh
    rcall     _printhexb
    pop       zl
;#{_printhexb
_printhexb:
    push      zl
    swap      zl        ; Do the upper nibble first
    rcall     _printhexnib
    pop       zl
_printhexnib:
    andi      zl,0x0f   ; Mask off the higher nibble
    subi      zl,-'0' ; Add ASCII bias.
    cpi       zl,0x3a   ; Was the value '0' to '9' ?
    brlo      _printhexnib1       ; Yes
    subi      zl,-7     ; No, adjust to 'A' to 'F'
_printhexnib1:
    mov       r24,zl
    rjmp      _txRS232
;#}
;
;#{_blanks
;#{_getdig
;#{_inpdec
;#{_inpdecdw
;#{_strtonum
;#{_getsign
_getsign:
	clt
	rcall	_blanks
	cpi 	zl,'-'
	brne	_getsignx
	set
	rjmp	_getdig
_getsignx:
	ret
;
_blanks:
	rcall     _getdig
	cpi       zl,0x20   ; skip leading spaces
	breq      _blanks
	cpi       zl,0x09   ; skip leading tabs
	breq      _blanks
	ret
;#}
;
;*******************************************
;* Put the previous char back into the Que *
;*******************************************
;#{_ungettty
_ungettty:
	lds 	zl,_rxRS232pos+1
	dec 	zl
	cpi  	zl,255
	brne 	_ungettty1
	ldi 	zl,RS232BSIZE-1
_ungettty1:
	sts 	_rxRS232pos+1,zl
	ret
;#}
;
;********************************************
;* Wait for 1 character from the 'host'.    *
;* Note: for a 'serious' controller, some   *
;* sort of 'time-out' trap should be used.  *
;********************************************
;#{_getdig
;#{_gettty
;#{_inpdec
;#{_inpdecdw
;#{_strtonum
;#{_inpstring
;#{_inpstringn
_getdig:
	lds       r0,_sysflags
	sbrc      r0,6
	rjmp      _getsdig
_gettty:
	rcall     _rxRS232  ; Any characters in the 'Rx holding buffer' ?
	breq      _gettty             ; None, wait for user input
	lds       r0,_sysflags
	tst       r0
	brpl      _noEcho
	mov       r24,zl
	cpi 	zl,0x0d
	brne      PC+2
	rjmp      _txRS232CR
	rjmp      _txRS232  ; Send the received character back to the user
_noEcho:
	sec
	ret
_getsdig:
	ld  	zl,x+
	ret
;#}
;
;*****************
;* input Hexword *
;* and BCD word  *
;*****************
;#{_hexbcd
;#{_inpbcd
;#{_inphex
_inpbcd:  
	set
    rjmp      _inphexalt
_inphex:
    clt
_inphexalt:
	push      r19
    clr       r19
    clr       r25
    clr       r20
    clr       r21
_inphex1:
    rcall     _blanks   ; skip any white space
    cpi       zl,'&'
	brne	_inphex2
_inphexalt1:
	rcall	_getdig
	rcall	_toupper
	cpi 	zl,'H'
	brne	_inphexx
_inphexlp:
	rcall	_getdig	; get the next hex byte
_inphex2:
	rcall	_toupper ; convert to UPPER case
	cpi	zl,'0'	; Was it < than '0' ?
	brlo	_inphexx ; Yes, exit
	cpi	zl,'G'	; Was it > than 'F' ?
	brsh	_inphexx ; Yes, we're done
	subi	zl,'0'    ; remove ASCII bias
	cpi	zl,10 ; Was the digit > '9' ?
	brlo	_inphex4 ; No, add it.
	brts	_inphexx ; Seperate the Hex/BCD calls
_inphex3:
	cpi 	zl,10     ; Was the Hex digit > '9'
	brlo	_inphex4 
	subi	zl,7      ; Yes, strip 'A' to 'F' difference
	cpi  	zl,10     ; Was it < 10 ?
	brlo	_inphexx ; Yes, we're done
_inphex4:
	rcall	_rol2
	rcall	_rol2
	rcall	_rol2
	rcall	_rol2
	add 	r19,zl
	rjmp	_inphexlp ; loop until all are processed
_inphexx:
	mov 	zl,r19	; Transfer results to primary Register.
	mov 	zh,r25
	mov 	r22,r20
	mov 	r23,r21
	pop 	r19
	ret
;
;******************************************
;* Convert upper case Alpha to lower case *
;******************************************
;#{_tolower:
_tolower:
          cpi       zl,'A'
          brlo      _tolowerx
          cpi       zl,'Z'+1
          brsh      _tolowerx
          subi      zl,-' '
_tolowerx:
          ret
;#}
;
;***************************
;* Divert output to String *
;* used by Prints          *
;***************************
;#{_setprintsb
;#{_setprints
;#{_clrprints
_setprintsb:
	clr  	xh
_setprints:
	lds 	zl,_sysflags
	sbr 	zl,0x10
	rjmp 	_setprints1
_clrprints:
	lds 	zl,_sysflags
	cbr 	zl,0x10
_setprints1:
	sts 	_sysflags,zl
	clr  	zl
	st  	x,zl
	ret
;#}
;********************************************
;* Ascii to decimal conversion routine	    *
;* Input a 'signed/unsigned' decimal number *
;* from the RS232 input channel, and convert*
;* to an 'integer' or 'word', 16bit value.  *
;********************************************
;#{_strtonum
_strtonum:			; get the VAL(str$)
	lds 	zl,_sysflags
	sbr 	zl,0x40	; Tell the getchar module that data comes from SRAM
	sts 	_sysflags,zl	; and NOT the RS232 input.
;#{_inpdec
;#{_inpdecdw
_inpdecdw:
_inpdec:
	push	r19
	clr 	r19		; Initial value = zero
	clr 	r25
	clr 	r20
	clr 	r21
	rcall 	_getsign
_inpdeclp:
	cpi 	zl,0x3a		; Is the digit > '9' ?
	brsh	_inpdecfin	; Yes, it's non-digit, we are done!
	cpi 	zl,0x30		; Is the digit < '0' ?
	brlo	_inpdecfin	; Yes, finished
	rcall	_adddig		; Good decimal 'digit', add it to the accumulator
	rcall 	_getdig
	rjmp	_inpdeclp	; Keep going. Note: you might add 'overflow' detection
_inpdecfin:
	mov 	zl,r19		; We are done, transfer the 'new' number to the primary register
	mov 	zh,r25
	mov 	r22,r20
	mov 	r23,r21
	brtc 	_inpdecx	; Is the 'T' flag (Neg) set ?
	com 	zl		; Yes, take the 2's complement, and save as '- number'
	com		zh
	com 	r22
	com 	r23
	subi	zl,-1
	sbci	zh,-1
	sbci	r22,-1
	sbci	r23,-1
_inpdecx:
	lds		r19,_sysflags	; Reset getdig source to default (RS232)
	cbr		r19,0x40
	sts		_sysflags,r19
	pop 	r19
	ret
;
;*******************************************
;* Multiply the current 'number' by 10 and *
;* add the 'new' digit                     *
;* Used by ALL Dec Input,including float   *
;*******************************************
;#{_adddig
_adddig:
	subi	zl,'0'	; Strip the Ascii bias and
	push	zl		;  save the 'new' digit to be added later.
	mov 	zl,r19	; Make a copy of original/existing number.
	mov 	zh,r25
	mov 	r22,r20
	mov 	r23,r21
	rcall	_rol2	; overall * 2
	rcall	_rol2	; overall * 4
	add 	r19,zl	; add the orig number, for overall * 5
	adc 	r25,zh
	adc 	r20,r22
	adc 	r21,r23
	rcall	_rol2	; overall * 10
	pop 	zl		; restore the 'new' digit
	clr 	zh
	add 	r19,zl	; Add in the new digit,
	adc 	r25,zh	; and 0+ any Carry that needs to be added
	adc 	r20,zh
	adc 	r21,zh
	ret
;#{_rol2
_rol2:
	lsl 	r19
	rol 	r25
	rol 	r20
	rol 	r21
	ret
;
;#}
;
;#{_inpstringn
_inpstringn:
	mov 	xl,zl
	mov 	xh,zh
_inpstringnlp:
	rcall	_rxRS232
	brcc 	_inpstringnlp
	st  	x+,zl
	dec  	r25
	brne 	_inpstringnlp
	ret
;#}
;
;#{_clrmem
_clrmem:
	mov 	xl,zl
	mov 	xh,zh
	clr  	zl
;#{_clrmem1
_clrmem1:
	st  	x+,zl
	sbiw	r24,1
	brne 	_clrmem1
	ret
;#}
;
;********************************************
;* Input Ascii characters from the 'host'   *
;* via RS232 & save them to an Sram 'array' *
;* This uses the 'x'(r26,r27) register pair *
;* as the (indirect) pointer to the Memory  *
;* 'target' location.		    *
;********************************************
;#{_inpstring
_inpstring:
	mov 	xl,zl
	mov 	xh,zh
_inpstringlp:
	rcall	_gettty
	cpi	zl,0x0d		; Is it a CR/(EOL) ?
	breq	_inpstrfin	; Yes, we are done!
	cpi	zl,0x0a		; Is it a Line Feed ?
	breq	_inpstringlp	; 'Toss' any line feeds out!
	st	X+,zl		; Save the 'new' character, x = x + 1
	rjmp	_inpstringlp	; Loop until end of input (EOL)
_inpstrfin:
	clr	zl		; Be sure to 'NULL' terminate string!
	st	X,zl
	ret			; return to caller
;#}
;
;#{_fmath
_fmul:
	ret
;
_fdiv:
	ret
;
_fsub:
_fadd:
	ret
_fmod:
	ret
;#}
;
;#{_fsqrt
_fsqrt:
	ret
;#}
;
;#{_flog
_flog:
	ret
;#}
;
;#{_add32
_add32:
	add 	zl,r24
	adc 	zh,r25
	adc 	r22,r20
	adc 	r23,r21
	ret
;#}
;
;#{_sub32
_sub32:
	sub 	zl,r24
	sbc 	zh,r25
	sbc 	r22,r20
	sbc 	r23,r21
	ret
;#}
;
;#{_mul10
_mul10:
	ldi  	r24,10
	clr  	r25
	rjmp 	_mulpwr
;#}
;	
;#{_mul100
_mul100:
	ldi 	r24,100
	clr 	r25
	rjmp 	_mulpwr
;#}
;
;#{_mul1000
_mul1000:
	ldi 	r24,low(1000)
	ldi 	r25,high(1000)
	rjmp 	_mulpwr
;#}
;
;*{_mul16s
;*{_div16s
;*{_mod16s
;*{_tst16s
;*{_neg16
_tst16s:
	sbrc	r31,7
	rjmp	_tst16s1
	sbrc	r25,7
	rjmp	_tst16s2
	clc
	ret
_tst16s1:
	rcall	_neg16
	sbrc	r25,7
	rjmp	_neg162
	sec
	ret
_tst16s2:
	rcall	_neg162
	sec
	ret
_neg162:
	com 	r24
	com 	r25
	adiw	r24,1
	clc
	ret
;#}
;
;#{_neg16
_neg16:
	com 	r30
	com 	r31
	adiw	r30,1
	ret
;#}
;
;#{_mul16s
_mul16s:
	rcall	_tst16s
	brcc	_mul16
	rcall	_mul16
	rjmp	_neg16
;#}
;
;#{_mul8
_mul8:
	clr 	zh
	clr 	r25
;#{_mul16
_mul16:
	clr 	r22
	clr 	r23
;#{_mulpwr
_mulpwr:
	clr 	r20
	clr 	r21
;#{_mul32
_mul32:
	push	r17
	push	r18
	push	r19
	push 	r26
	push 	r27
	clr 	r18
	clr 	r19
	clr 	r26
	clr 	r27
	ldi 	r17,32		; make a counter
	lsr 	r21
	ror 	r20
	ror 	r25
	ror 	r24
_m32a:
	brcc	_m32b		; skip if no bit
	add 	r18,zl		; add multiplicand to partial product
	adc 	r19,zh
	adc 	r26,r22
	adc 	r27,r23
_m32b:
	ror 	r27 		; right shift partial product
	ror 	r26
	ror 	r19
	ror 	r18	

	ror 	r21
	ror 	r20	
	ror 	r25	
	ror 	r24	
	dec 	r17 		; all done ?
	brne	_m32a		; yes
	mov 	zl,r24
	mov 	zh,r25
	mov 	r22,r20
	mov 	r23,r21
	pop 	r27
	pop 	r26
	pop 	r19
	pop 	r18
	pop 	r17
	ret
;#}
;
;********************************************
;* Divide primary register by 10	    *
;********************************************
;#{_div10
;#{_div100
_div100:
	ldi 	r24,100
	rjmp 	_divpwr
;#}
;
;#{_div1000
_div1000:
	ldi 	r24,low(1000)
	ldi 	r25,high(1000)
	rjmp 	_divpwr1
;#}
;
;#{_div10
_div10:
	ldi 	r24,10	; Put '10' into secondary 
_divpwr:
	clr 	r25
_divpwr1:
	clr 	r20
	clr 	r21
	rjmp	_div32	; And do normal 32bit division
;#}
;
;#{_div16s
_div16s:
	rcall	_tst16s
	brcc	_div16
	rcall	_div16
	rjmp	_neg16
;#{_mod8
;#{_div8
_div8:
	clr 	zh
	clr 	r25
;#{_mod16
;#{_div16
_div16:
	clr 	r20
	clr 	r21
	clr 	r22
	clr 	r23
;#{_avrgw
;#{_div10
;#{_mod32
;#{_div32
_div32:
	push	r17
	push	r18
	push	r19
	push 	r26
	push 	r27
	clr 	r18
	clr 	r19
	clr 	r26
	sub 	r27,r27
	ldi 	r17,33
_div32_1:
	rol 	zl
	rol 	zh
	rol 	r22
	rol 	r23
	dec 	r17
	brne 	_div32_2
	mov 	r24,r18
	mov 	r25,r19
	mov 	r20,r26
	mov 	r21,r27
	pop 	r27
	pop 	r26
	pop 	r19
	pop 	r18
	pop 	r17
	ret
_div32_2:
	rol 	r18
	rol 	r19
	rol 	r26
	rol 	r27
	sub 	r18,r24
	sbc 	r19,r25
	sbc 	r26,r20
	sbc 	r27,r21
	brcc	_div32_3
	add 	r18,r24
	adc 	r19,r25
	adc 	r26,r20
	adc 	r27,r21
	clc
	rjmp	_div32_1
_div32_3:
	sec
	rjmp 	_div32_1
;#}
;
;#{_mod8
_mod8:
	rcall	_div8
	rjmp	_modall
;#{_mod16
_mod16:
	rcall	_div16
	rjmp	_modall
;#{_mod32
_mod32:
	rcall	_div32
_modall:
	mov 	zl,r24
	mov 	zh,r25
	mov 	r22,r20
	mov 	r23,r21
	ret
;#}
;
;#{_inc_dw
;#{_dec_dw
_get_dw:
	mov  	r26,r30
	mov  	r27,r31
	ld  	r30,X+
	ld  	r31,X+
	ld  	r22,X+
	ld  	r23,X+
	ret
;
_inc_dw:
	rcall	_get_dw
	adiw 	r30,1
	sbci 	r22,-1
	sbci 	r23,-1
	ret
;
_dec_dw:
	rcall	_get_dw
	sbiw 	r30,1
	sbci 	r22,0
	sbci 	r23,0
	ret
;#};
;
;#{_getabyteb
_getabyteb:
	clr 	r25
;#{_getabyte
_getabyte:
	add 	xl,r24
	adc 	xh,r25
	ld  	zl,x+
	clr 	zh
	ret
;#}
;
;#{_getabyteb2
_getabyteb2:
	clr 	r25
;#{_getabyte2
_getabyte2:
	add 	xl,r24
	adc 	xh,r25
	ld  	r24,x+
	clr 	r25
	ret
;#}
;
;#{_putabyteb
_putabyteb:
	clr 	r25
;#{_putabyte
_putabyte:
	add 	xl,r24
	adc 	xh,r25
	st  	x+,zl
	ret
;#}
;
;#{_getawordb
;#{_getawordb2
;#{_getaword
;#{_getaword2
;#{_putawordb
;#{_putaword
_wgpoffs:
	lsl 	r24
	rol 	r25
	add 	xl,r24
	adc 	xh,r25
	ret
;#}
;
;#{_getawordb2
_getawordb2:
	clr 	r25
;#{_getaword2
_getaword2:
	rcall 	_wgpoffs
	ld  	r24,x+
	ld  	r25,x+
	ret
;#}
;
;#{_getawordb
_getawordb:
	clr 	r25
;#{_getaword
_getaword:
	rcall 	_wgpoffs
	ld  	zl,x+
	ld  	zh,x+
	ret
;#}
;
;#{_putawordb
_putawordb:
	clr 	r25
;#{_putaword
_putaword:
	rcall 	_wgpoffs
	st  	x+,zl
	st  	x+,zh
	ret
;#}
;
;********************************
;* Used by the On Gosub keyword *
;********************************
;#{_oncallit
_oncallit:
	cp	r24,r25
	brsh	_oncallitx
	clr	r25
	add	zl,r24
	adc	zh,r25
	ijmp
_oncallitx:
	ret
;#}
;*******************************
;* Used by the On Goto keyword *
;*******************************
;#{_ongotoit
_ongotoit:
	cp	r24,r25
	brsh	_oncallitx
_ongotoit1:
	clr	r25
	add	zl,r24
	adc	zh,r25
	ijmp
_ongotoitx:
	pop	r24
	pop	r24
	mov	r24,r25
	rjmp _ongotoit1
;#}
;
;********************************************
;* The following routines are for JAVRBasic *
;* and may not be of any interest. It uses  *
;* 'Inverted' logic. (0=true, 1=false)	    *
;* I wish the AVR had the 'cpsne' op	    *
;********************************************
;*** bool 'compare' logic for the 'cpse' op ***
;_forlteb:
;	cp	zl,r24
;	rjmp	_forlte1
;_forlte:
;	cp	zl,r24
;	cpc	zh,r25
;_forlte1:
;	brlo	_false
;	breq	_false
;	rjmp	_true
;_forgteb:
;	cp	zl,r24
;	rjmp	_forgte1
;_forgte:
;	cp	zl,r24
;	cpc	zh,r25
;_forgte1:
;	brlo	_true
;	rjmp	_false
;_strgt:
;	rcall	_stringcompare
_gtb:
	cp	zl,r24
	rjmp	_gt1
_gt:
	cp	zl,r24
	cpc	zl,r25
_gt1:
	breq	_false
	brsh	_true
	rjmp	_false
;_strgteq:
;	rcall	_stringcompare
_gteqb:
	cp	zl,r24
	rjmp	_gteq1
_gteq:
	cp	zl,r24
	cpc	zh,r25
_gteq1:
	brsh	_true
	rjmp	_false
;_strlt:
;	rcall	_stringcompare
_ltb:
	cp	zl,r24
	rjmp	_lt1
_lt:
	cp	zl,r24
	cpc	zh,r25
_lt1:
	brlo	_true
	rjmp	_false
;_strlteq:
;	rcall	_stringcompare
_lteqb:
	cp	zl,r24
	rjmp	_lteq1
_lteq:
	cp	zl,r24
	cpc	zh,r25
_lteq1:
	brlo	_true
	breq	_true
_false:
	clr	r25
	ldi	r24,1
	clz
	ret
;_strneq:
;	rcall	_stringcompare
_neqb:
	cp	zl,r24
	rjmp	_neq1
_neq:
	cp	zl,r24
	cpc	zh,r25
_neq1:
	brne	_true
	rjmp	_false
;_streq:
;	rcall	_stringcompare
_eqb:
	cp	zl,r24
	rjmp	_eq1
_eq:
	cp	zl,r24
	cpc	zh,r25
_eq1:
	brne	_false
_true:
	clr	r24
	clr	r25
	ret
;
;********************************************
;* write your own Random Number (LSRs etc.) *
;********************************************
;#{_random
_random:
	ret
;#}
;
;#{_caseto
;#{_casetob
_casetob:
	cp  	zl,r24
	brlo	_casefalse
	cp  	r20,zl
	brlo 	_casefalse
	sez
	ret
_caseto:
	cp  	zl,r24
	cpc 	zh,r25
	brsh	_casetoa
_casefalse:
	clz
	ret
_casetoa:
	cp  	r20,zl
	cpc 	r21,zh
	brlo	_casefalse
	sez
	ret
;#}
;*** end of compare logic ***
;****************************
;* Bit shifting routines    *
;****************************
;#{_shlb
_shlb:
	dec  	r24
	brmi 	_shlbx	; handle << 0
	lsl  	zl
	rjmp	_shlb
_shlbx:
	ret
;#}
;
;#{_shlw
_shlw:
	dec  	r24
	brmi 	_shlwx
	lsl  	zl
	rol  	zh
	rjmp	_shlw
_shlwx:
	ret
;#}
;
;#{_shrb
_shrb:
	dec  	r24
	brmi 	_shrbx	; handle >> 0
	lsr  	zl
	rjmp	_shrb
_shrbx:
	ret
;#}
;
;#{_shrw
_shrw:
	dec  	r24
	brmi 	_shrwx
	lsr  	zh
	ror  	zl
	rjmp	_shrw
_shrwx:
	ret
;#}
;
;#{_flashtostr
_flashtostr:
	lpm
	adiw	zl,1
	st  	x+,r0
	and  	r0,r0
	brne 	_flashtostr
	ret
;#}
;
;#{_avrgw
_avrgw:
	push	yl
	push	yh

	push	r24
	push	r25
	mov 	xl,zl
	mov 	xh,zh
	mov 	yl,zl
	mov 	yh,zh
	mov 	r23,r22
	dec  	r23
	clr	r22
	ld  	zl,x+
	ld  	zh,x+
	add 	zl,r24	; sneak in new item to be averaged
	adc 	zh,r25
	sbci	r22,-1
_avrgwlp:
	ld  	r24,x+
	ld  	r25,x+
	add 	zl,r24
	adc 	zh,r25
	sbci	r22,-1
	st  	y+,r24	; bump first Array item w/2nd etc. "Bubble down"
	st  	y+,r25
	dec 	r23
	brne	_avrgwlp
	mov 	r24,r22
	clr 	r25
	clr 	r20
	clr	r21
	clr	r23
	rcall	_div32
	pop 	r25
	pop 	r24
	st  	-y,r25	; append new value to last Array item
	st  	-y,r24
	pop  	yh
	pop  	yl
	ret  		; return (zl,zh) = new average
;#}
;
;*********************************
;* Universal EEPROM Read & Write *
;*********************************
;#{_EERD_dw
;#{_EERD_w
;#{_EERD_b
;#{_EERD_n
;#{_EERD_nz
_EERD_dw:
	rcall	_EERD_w
	push	r31
	push	r30
	rcall	_EERD_w
	mov 	r22,r30
	mov 	r23,r31
	pop 	r30
	pop 	r31
	ret
;
_EERD_w:
	rcall	_EERD_byte
	push	r0
	rcall	_EERD_byte
	mov 	r31,r0
	pop 	r30
	ret
;
_EERD_b:
	rcall	_EERD_byte
	mov 	r30,r0
	clr  	r31
	ret
_EERD_byte:
	sbic	EECR,EEWE
	rjmp	_EERD_byte
	out 	EEARL+1,r25
	out 	EEARL,r24
	sbi		EECR,EERE
	sbi		EECR,EERE
	in  	r0,EEDR
	adiw	r24,1
	ret
;
_EERD_nz:
	rcall	_EERD_n
	st  	z,r20
	ret
_EERD_n:
	rcall	_EERD_byte
	st  	z+,r0
	dec 	r20
	brne	_EERD_n
	ret
;#}
;**************************
;* Universal EEPROM Write *
;**************************
;#{_EEWR_w
;#{_EEWR_b
;#{_EEWR_dw
;#{_EEWR_n
;#{_EEWR_nz
_EEWR_w:
	rcall	_EEWR_b
	mov 	r0,r31
	rjmp 	_EEWR_b1
_EEWR_b:
	mov 	r0,r30
_EEWR_b1:
	cli
_EEWR_b2:
	sbic	EECR,EEWE
	rjmp	_EEWR_b2
	out 	EEARL+1,r25	; for the 8515.  This is a dummy for the 2313
	out 	EEARL,r24
	out 	EEDR,r0
	sbi 	EECR,EEMWE
	sbi 	EECR,EEWE
	adiw	r24,1
	clr  	r0
_EEWR_b3:
	dec  	r0
	brne	_EEWR_b3
	sei
	ret
;
_EEWR_nz:
	rcall	_EEWR_n
	clr  	r0
	rjmp	_EEWR_b1
_EEWR_dw:
	ldi 	r20,4
_EEWR_n:
	ld  	r0,z+
	rcall	_EEWR_b1
	dec 	r20
	brne	_EEWR_n
	ret
;#}
;
;#{_ATOD_Init
;#{_atodb
;#{_atod
_ATOD_Init:
	out  	ADCSR,zl
	mov  	zl,r24
	rjmp 	_atod
;
_atoda:
	in  	zl,ADMUX
_atod:
	out  	ADMUX,zl
	cbi  	ADCSR,ADIF
	sbi  	ADCSR,ADSC
	rcall 	_atodwait
	sbi  	ADCSR,ADSC
_atodwait:
	cli
_atodwaitlp:
	sbis	ADCSR,ADIF
	rjmp  	_atodwaitlp
	in  	zl,ADCL
	in  	zh,ADCH
	cbi  	ADCSR,ADIF
	sei
	ret
;
;***************************
;* get MSB 8bits and round *
;***************************
_atodab:
	in  	zl,ADMUX
_atodb:
	rcall 	_atod
	lsr  	zh
	ror  	zl
	lsr  	zh
	ror  	zl
	ret
;#}
;
;#{_SPI_Init
_SPI_Init:
	ret
;#}
;#{_spiRD
	ret
;
;#{_spiRDb
	ret
;#}
;#{_spiRDw
	ret
;#}
;#{_spiWRb
	ret
;#}
;#{_spiWRw
	ret
;#}
;#{_I2C_Init
_I2C_Init:
	ret
;#}
;#{_i2cRD
_i2cRD:
	ret
;
;#{_i2cRDb
_i2cRDb:
	ret
;#}
;#{_i2cRDw
_i2cRDw:
	ret
;#}
;#{_i2cWRb
_i2cWRb:
	ret
;#}
;#{_i2cWRw
_i2cWRw:
	ret
;#}


