.include "4433def.inc"

;*****************************************************************************************
;	Variablen
;*****************************************************************************************
.def	TimerLow		= r08	; Register to start actions at defined times
.def	TimerHigh		= r09
.def	AdcTempL		= r10	; Register to store the Measured Current
.def	AdcTempH		= r11
.def	AddrSave		= r12	; Address for indirect saving of Parameters
.def	Drehzahl		= r13	; received Drehzahl value
.def	I_up			= r14	; received I_up value
.def	I_down			= r15	; received I_down value
.def	DelayTemp		= r16	; Temporary Variable for Delay Routines
.def	Temp			= r17	; Temporary Variable
.def	Temp2			= r18	; Temporary Variable
;.def	Temp3			= r19	; Temporary Variable
.def	Saa_Address		= r20	; address variable, 03 is control, 04 is initialisation data
.def	Saa_R0			= r21	; data variable, Register 0
.def	Saa_R1			= r22	; data variable, Register 1
.def	Saa_R2			= r23	; data variable, Register 2
.def	Statusprog1		= r24	; Status Register 1
			; Bits 3210		0000	TX Current activated
			;				0010	TX Current Key finished
			;				0100	TX Current 1000er value finished
			;				0110	TX Current 100er value finished
			;				1000	TX Current 10er value finished
			;				1010	TX 255 activated
			; Bits	54		00		receive Key
			;				01		key received, receive address
			;				10		address received, receive value
.def	Statusprog2		= r25	; Status Register 2
			; Bit	0		1		Frequency is changed
			; Bit	1		1		Frequency is decreased
			; Bit	2		1		Current exceeds I_Up Value
			; Bit	3		1		Start ADC_POSTROUTINE
			; Bit	4		1		RX Value finished, Call RX Value Postroutine
			; Bit	5		1		ADC Postroutine not finished, skip ADC during whole cycle
			; Bit	6		1		Preroutines for Current transmission to Display start

;*****************************************************************************************
;	Werte
;*****************************************************************************************
.equ	SAA_PORTH     	= PORTD
.equ	SAA_DDRH     	= DDRD
.equ	SAA_PINH     	= PIND
.equ	SAA_PORTL     	= PORTC
.equ	SAA_DDRL     	= DDRC
.equ	SAA_PINL     	= PINC
.equ	SAA_CRTL_PORT	= PORTB
.equ	SAA_CRTL_DDR	= DDRB
.equ	SAA_CRTL_PIN	= PINB
	; Control Lines for the Saa
.equ	SAA_ALE			= 0
.equ	SAA_RDN			= 1
.equ	SAA_WRN			= 2
.equ	SAA_CSN			= 3
.equ	SAA_RSTN		= 4
.equ	ADC_PORT		= PORTC
.equ	ADC_DDR			= DDRC
.equ	ADC_PIN			= PINC
	; Bits of StatusProg2
.equ	ChangeRpm		= 0
.equ	RpmDown			= 1
.equ	OverCurrent		= 2
.equ	ADCFinished		= 3
.equ	RXFinished		= 4
.equ	ADCSkip			= 5
.equ	TXInit			= 6

;*****************************************************************************************
;	SRAM Variablen
;*****************************************************************************************
.equ	DrehzLowAkt		= 0x60	; actual Drehzahl Value
.equ	DrehzHighAkt	= 0x61
.equ	Amp				= 0x62	; actual Amplitude Value
.equ	DrehzLowSoll	= 0x63	; Drehzahl soll Value
.equ	DrehzHighSoll	= 0x64
.equ	Curr1000		= 0x65	; Converted Current values to send to Display
.equ	Curr100			= 0x66
.equ	Curr10			= 0x67
.equ	Curr1			= 0x68
.equ	ConvLow			= 0x69	; ADC values to be converted into Curr1000..Curr1
.equ	ConvHigh		= 0x6a

;*****************************************************************************************
;	EEPROM Variablen
;*****************************************************************************************

;*****************************************************************************************
;	Programmcode
;*****************************************************************************************
.cseg
.org	$0000

;*****************************************************************************************
;	Interruptroutinen
;*****************************************************************************************
		rjmp	START		; Reset Vektor
		reti				; Int0 Pin
		reti				; Int1 Pin
		reti				; Timer1 Capture Event
		reti				; Timer1 Compare Event
		reti				; Timer1 Overflow
		rjmp	TMR0_ROUTINE; Timer0 Overflow
		reti				; SPI Transfer Complete
		rjmp	RX_ROUTINE	; UART RX Complete
		rjmp	TX_ROUTINE	; UART Data Register Empty
		reti				; UART TX Complete
		rjmp	ADC_ROUTINE	; ADC Conversion complete
		reti				; EEPROM ready
		reti				; Analog Comparator

;*****************************************************************************************
;	Programmstart
;*****************************************************************************************
START:
		ldi		Temp, low(RAMEND)		; Initialize Stackpointer
    	out   	SPL, Temp
		cli								; Disable all Interrupts
    	;ldi   	Temp, high(RAMEND)
    	;out   	SPH, temp
		ldi		Temp, 0b00011001		; disable the watchdog timer if it is running
		out		WDTCR, Temp
		andi	Temp, 0b11100111
		out		WDTCR, Temp				; disable it!
		clr		StatusProg1				; clear StatusProg Registers
		clr		StatusProg2
		clr		Temp
		out		GIMSK, Temp				; Disable Interrupts Int0 and Int1
		out		TIMSK, Temp				; Disable all Timer Interrupts
		mov		Drehzahl, Temp			; set drehzahl to zero
		sts		DrehzLowAkt, Temp		; set all drehzahl and amplitude registers to zero
		sts		DrehzHighAkt, Temp
		sts		Amp, Temp
		sts		DrehzHighSoll, Temp
		sts		DrehzLowSoll, Temp
		ldi		Temp, 20
		mov		I_Down, Temp			; initial Current Values
		ldi		Temp, 5
		mov		I_Up, Temp
		ldi		Temp, 0b11111110
		out		ADC_DDR, Temp			; set all ADC inputs to 0 level and outtput (not the input itself)
		clr 	Temp
		out		ADC_PORT, Temp
		ldi		Temp, 0b00011111		; set SAA control Pins as outputs
		out		SAA_CRTL_DDR, Temp
		cbi		SAA_CRTL_PORT, SAA_RSTN
		sbi		SAA_CRTL_PORT, SAA_CSN	; saa Ruhezustand
		sbi		SAA_CRTL_PORT, SAA_RDN
		sbi		SAA_CRTL_PORT, SAA_WRN
		cbi		SAA_CRTL_PORT, SAA_ALE
		ldi		Temp, 0b11111100
		out		SAA_DDRH, Temp			; set SAA Data outputs, DDRL is already set at ADC
		rcall	DELAY775				; startup time for SAA
		rcall	SAA_INIT				; initialise Saa
		ldi		Saa_R0, 0x00			; lower 8 bits of frequency
		ldi		Saa_R1, 0b00000000		; some bits and higher 4 bits of frequency, output inhibited
		ldi		Saa_R2, 0x00			; Amplitude
		ldi		Saa_Address, 0x03		; write as control data
		rcall	SAA_TRANSMIT
		sbi		SAA_CRTL_PORT, SAA_RSTN	; disable Saa reset
		ldi		Temp, 100
		out		UBRR, Temp				; set the Baud Rate very low (((9600 at 4,433MHz Clock
		cbi		UCSRA, MPCM				; only single serial protocol
;		ldi		Saa_R0, 0x00			; lower 8 bits of frequency
;		ldi		Saa_R1, 0b00100000		; some bits and higher 4 bits of frequency
;		ldi		Saa_R2, 0x00			; Amplitude
;		ldi		Saa_Address, 0x03		; write as control data
;		rcall	SAA_TRANSMIT			; may be not necessary
		ldi		Temp, 0b10001110		; aden, adsc, adfr, adif, adie, adprescale2..0
		out		ADCSR, Temp
		ldi		Temp, 0b00000000
		out		ADMUX, Temp				; set admux to input 0
		ldi		Temp, 0b10011000
		out		UCSRB, Temp				; enable serial input and output and UART RX interrupt
		sei								; enable interrupts
		ldi		Temp, 0b00000010
		out		TCCR0,Temp				; Set Timer 0 Prescaler to Clk/8
		ldi		StatusProg1, 0b00001010	; request data from control unit(set TX 255 State and enable UDRIE)
		sbi		UCSRB, UDRIE
		ldi		Temp, 0b00000010
		out		TIMSK,Temp	 			; enable the Counter 0 interrupt

forever:
		sbrc	StatusProg2, RXFinished	; Verify Routines
		rcall	RX_DREHZAHL_POST		; Calculate and Soll Frequency Amplitude after Frequency is received
		sbrc	StatusProg2, ADCFinished
		rcall	ADC_POSTPROCESS			; Postprocess measured values after ADC finished
		sbrc	StatusProg2, TXInit		; initialise the TX Current Routine 
		rcall	TX_INIT
		rjmp	forever

;*****************************************************************************************
;	Interrupt-Routinen
;*****************************************************************************************

TMR0_ROUTINE:						; called with 4kHz
		push	Temp
		push	Temp2
		ldi		Temp, 1
		clr		Temp2
		add		TimerLow, Temp
		adc		TimerHigh, Temp2		; increase timer counter register
		ldi		Temp, 0b00000010
		cp		TimerHigh, Temp
		brlo	TMR0_NO_VERIFY_VALUE
		clr		Temp
		cp		Temp, TimerLow
		breq	TMR0_SEND_ACTIVATE		; activate Send Value Routine with every 513 timer cycles
 TMR0_NO_VERIFY_VALUE:
		mov		Temp2, TimerLow			; make backup
		andi	Temp2, 0b00001111		; only use the Bits 0..3 (max. 16)
		ldi		Temp, 0b00001000		; ADC_POSTROUTINE is called every 16 Timer events
		cp		Temp2, Temp
		breq	TMR0_ADC_POST_ACTIVATE	; call the ADC Postroutine
		brlo	TMR0_ADC_ACTIVATE		; activate ADC Start Routine at Register Values 0..7
		rjmp	TMR0_ROUTINE_OUT
 TMR0_SEND_ACTIVATE:
		ori		StatusProg2, 0b01000000	; set the activate TX Bit
 		clr		Temp
		mov		TimerHigh, Temp			; reset the Timer Bytes after Routine
		mov		TimerLow, Temp
 		rjmp	TMR0_ROUTINE_OUT
 TMR0_ADC_POST_ACTIVATE:
		ori		StatusProg2, 0b00001000	; set the ADC Call Postroutine Bit
 		rjmp	TMR0_ROUTINE_OUT
 TMR0_ADC_ACTIVATE:
		clr		Temp
		cp		Temp2, Temp
		brne	TMR0_ADC_NO_ZERO_CYCLE	; control if postprocess is running before first Conversion
		sbrc	StatusProg2, ADCFinished
		ori		StatusProg2, 0b00100000	; set Skip ADC Bit if Postprocess is running
  TMR0_ADC_NO_ZERO_CYCLE:
		sbrs	StatusProg2, ADCSkip		; do conversion only if Postprocess is in time
 		sbi		ADCSR, ADSC				; Activate ADC Start Conversion Bit
		cpi		Temp2, 0b00000111
		brne	TMR0_ADC_NO_SKIP_RESET
		andi	StatusProg2, 0b11011111	; reset Skip ADC Bit in last ADC Cycle
  TMR0_ADC_NO_SKIP_RESET:
 		rjmp	TMR0_ROUTINE_OUT		
 TMR0_ROUTINE_OUT:
		ldi		Temp, 128
		out		TCNT0, Temp				; Set Timer 0 to 128 cycles
		pop		Temp2
		pop		Temp
		reti

RX_ROUTINE:							; receives exactly one value, see StatusProg1 Bits 4,5
		push	Temp
		push	Temp2
		in		Temp2, UDR				; Read data
		sbic	UCSRA, FE				; test if frame error
		rjmp	RX_KILL_OUT				; go out if Frame error occured
		sbic	UCSRA, OR
		rjmp	RX_KILL_OUT				; go out if overrun occured
		mov		Temp, Statusprog1
		andi	Temp, 0b00110000		; use Bits 5 and 4
		cpi		Temp, 0b00000000		; go to the right subroutines
		breq	RX_KEY
		cpi		Temp, 0b00010000
		breq	RX_ADDRESS
		cpi		Temp, 0b00100000
		breq	RX_VALUE
		rjmp	RX_KILL_OUT
 RX_KEY:
 		cpi		Temp2, 255
		brne	RX_KILL_OUT				; go out
		ori		StatusProg1, 0b00010000	; Set Key received Value
		rjmp	RX_OUT					; go out
 RX_ADDRESS:
		cpi		Temp2, 5				; test if one of the three registers are addressed
		brsh	RX_KILL_OUT
		cpi		Temp2, 2
		brlo	RX_KILL_OUT
		mov		AddrSave, Temp2			; save Address info
		andi	Statusprog1, 0b11101111
		ori		StatusProg1, 0b00100000	; Set address received Value
 		rjmp	RX_OUT
 RX_VALUE:
 		push	ZL
		push	ZH
		ldi		ZL, 11					; get value for indirect addressing of Drehzahl.. I_Down
		clr		ZH
		add		ZL, AddrSave
		st		Z, Temp2				; store value as Drehzahl, I_up or I_down
		ori		StatusProg2, 0b00010000	; set RX Value finished Bit
		pop		ZH
		pop		ZL
 		rjmp	RX_KILL_OUT				; always clear StatusProg1
 RX_KILL_OUT:
 		andi	Statusprog1, 0b11001111	; clear StatusProg1
 RX_OUT:
 		pop		Temp2
 		pop		Temp
		reti

TX_ROUTINE:							; sends exactly one value via UART, See StatusProg1 for value
		push	Temp
		mov		Temp, StatusProg1
		andi	Temp, 0b00001111		; use Bits 3..0 of StatusProg1
		cpi		Temp, 0b00000000
		breq	TX_CURR_KEY
		cpi		Temp, 0b00000010
		breq	TX_CURR_1000_VAL
		cpi		Temp, 0b00000100
		breq	TX_CURR_100_VAL
		cpi		Temp, 0b00000110
		breq	TX_CURR_10_VAL
		cpi		Temp, 0b00001000
		breq	TX_CURR_1_VAL
		cpi		Temp, 0b00001010
		breq	TX_255_KEY
		cbi		UCSRB, UDRIE			; Disable UART Empty Interrupt to stop Transfer
		rjmp	TX_OUT
 TX_CURR_KEY:
		ldi		Temp, 254				; load the TX Current Key
		ori		StatusProg1, 0b00000010	; Set TX Key finished Signal
		rjmp	TX_IT
 TX_CURR_1000_VAL:
 		lds		Temp, Curr1000			; load 1000er value
		andi	StatusProg1, 0b11111101
		ori		StatusProg1, 0b00000100 ; Set 1000er value finished Signal
		rjmp	TX_IT
 TX_CURR_100_VAL:
 		lds		Temp, Curr100			; load 100er value
		ori		StatusProg1, 0b00000010 ; Set 100er value finished Signal
		rjmp	TX_IT
 TX_CURR_10_VAL:
		lds		Temp, Curr10			; load 10er value
		andi	StatusProg1, 0b11111001
		ori		StatusProg1, 0b00001000 ; Set 10er value finished Signal
		rjmp	TX_IT
 TX_CURR_1_VAL:
		lds		Temp, Curr1				; load 1er value
		andi	StatusProg1, 0b11110001	; clear Status Bits
		cbi		UCSRB, UDRIE			; Disable UART Empty Interrupt to stop Transfer
		rjmp	TX_IT
 TX_255_KEY:
 		ldi		Temp, 255
		andi	StatusProg1, 0b11110001	; clear Status Bits
		cbi		UCSRB, UDRIE			; Disable UART Empty Interrupt to stop Transfer
		rjmp	TX_IT
 TX_IT:
 		out		UDR, Temp				; send Temp value
 TX_OUT:
		pop		Temp
		reti

ADC_ROUTINE:						; Reads the ADC and sets ADC Finished Bit
									;	adds the contents of the ADC to AdcTemp to make averaging
		push	ZL
		push	ZH		
		in		ZL, ADCL				; read ADC data
		in		ZH, ADCH
		add		AdcTempL, ZL			; Add new value to ADC registers(averaging)
		adc		AdcTempH, ZH
 ADC_ROUTINE_OUT:
 		pop		ZH
		pop		ZL
		reti

;*****************************************************************************************
;	Unterprogramme
;*****************************************************************************************

ADC_POSTPROCESS:					; Divides AdcTemp by 8(averaging) and increases or decreases
									;	Drehzahl according to the measured current
									;	AdcTemp is cleared in this routine
		cli
		push	ZH
		push	ZL
		push	YH
		push	YL
		push	Temp
		mov		ZL, AdcTempL			; recall converted values
		mov		ZH, AdcTempH
		andi	StatusProg2, 0b11111000	; Reset all Change Rpm Bits
		clr		AdcTempL
		clr		AdcTempH
		andi	StatusProg2, 0b11110111	; Clear ADC Postprocess Bit as Values are read out
		lsr		ZH						; divide by 8 (averaging)
		ror		ZL
		lsr		ZH
		ror		ZL
		lsr		ZH
		ror		ZL
		sts		ConvLow, ZL				; store values for transmission to display
		sts		ConvHigh, ZH			; works well as only ADC Post OR TX init is running
		lsr		ZH						; divide by 4(to get the range 255)
		ror		ZL
		lsr		ZH
		ror		ZL
		cp		ZL, I_Down
		brsh	ADC_POST_OVERCURRENT	; Current is larger than Max Value
		cp		ZL, I_Up
		brlo	ADC_POST_UNDERCURRENT	; Current is lower than Speed up value
 ADC_POST_OVERDREHZAHL:					; Check if Drehzahl is larger than soll value
		lds		ZL, DrehzLowAkt			; load actual and Soll Drehzahl
		lds		ZH, DrehzHighAkt
		lds		YH,	DrehzHighSoll
		lds		YL,	DrehzLowSoll
		sub		YL, ZL
		sbc		YH, ZH
		brlo	ADC_POST_SPIN_DOWN		; Actual Drehzahl is larger than Soll Drehzahl, spin down

 ADC_POST_FINISH:						; restore values and go out
		pop		Temp
		pop		YL
		pop		YH
		pop		ZL
		pop		ZH
		andi	StatusProg2, 0b11110111	; Reset ADC finished Bit
		sei
		ret
 ADC_POST_OVERCURRENT:
 		ori		StatusProg2, 0b00000100	; Set Overcurrent Bit
 ADC_POST_SPIN_DOWN:
 		ori		StatusProg2, 0b00000011	; Set Rpm Down and Change Rpm Bits
		lds		ZL, DrehzLowAkt			; load actual Drehzahl
		lds		ZH, DrehzHighAkt
		sbiw	ZL, 1					; decrease Drehzahl
		brlo	RESET_ALL				; reset all when actual Drehzahl is 0 and called to decrease
		sts		DrehzLowAkt, ZL			; save Drehzahl
		sts		DrehzHighAkt, ZH
		rcall	CALCAMP					; Calculate Amplitude
		rcall	SAA_SEND_CONTROL		; Send frequency and Amplitude to Saa
 		rjmp	ADC_POST_FINISH			; jump to end of ADC Postroutine
 ADC_POST_UNDERCURRENT:
		lds		ZL, DrehzLowAkt			; load actual Drehzahl
		lds		ZH, DrehzHighAkt
		lds		YH,	DrehzHighSoll		; load Soll Drehzahl
		lds		YL,	DrehzLowSoll
		adiw	ZL, 1					; increase Drehzahl
		sub		YL, ZL
		sbc		YH, ZH
		brlo	ADC_POST_OVERDREHZAHL	; go out if Soll Drehzahl reached
		ori		StatusProg2, 0b00000001	; Set Rpm Change Bit
		sts		DrehzLowAkt, ZL			; save Drehzahl
		sts		DrehzHighAkt, ZH
		rcall	CALCAMP					; Calculate Amplitude
		rcall	SAA_SEND_CONTROL		; Send frequency and Amplitude to Saa
		rjmp	ADC_POST_FINISH			; jump to ADC finished routine after Drehzahl decreased

RX_DREHZAHL_POST:					; Postroutine after a drehzahl value is received
									;	calculates the corresponding Soll Value
		rcall	CALCFSOLL				; calculate Soll frequency
		andi	StatusProg2, 0b11101111	; reset RX-Drehzahl Post Call Routine
		ret

 RESET_ALL:							; activates the Wathdog to reset the chip and start again
 		ldi		Temp, 0b00001000		; enable the watchdog with smallest possible reset time
		out		WDTCR, Temp
  RESET_INNER:
  		rjmp	RESET_INNER

TX_INIT:
		push	ZH
		push	ZL
		andi	StatusProg2, 0b10111111	; reset the activate TX Bit
		lds		ZH, ConvHigh
		lds		ZL, ConvLow				; load the Conversion Values into Z
		rcall	CONV10BIT				; convert to decimal
		pop		ZL
		pop		ZH
 		sbi		UCSRB, UDRIE			; just enable the UDRE Interrupt. If No TX is running
										; TX_CURRENT will start. Otherwise the running Routine
										; will be finished.
		ret

;*****************************************************************************************
;	Conversion Routinen
;*****************************************************************************************

CONV10BIT:							; convert a hex value smaller than 10000 into decimal value
										; put the value in ZH:ZL
										; result is stored in Curr1000...Curr1
		push	ZH
		push	ZL
		push	YH
		push	YL
		push	Temp
		ldi		YL, 0xE8				; load 1000 into YH:YL
		ldi		YH, 0x03
		clr		Temp
 CONVINNER1000:
		sub		ZL, YL					; Subtraction
		sbc		ZH, YH
		brcs	CONV1000OUT				; exit if negative
		inc		Temp					; increase Result
		rjmp	CONVINNER1000
 CONV1000OUT:
 		add		ZL, YL					; undo last Subtraction
		adc		ZH, YH
		clr		YH 						; Pointer to 1000er value
		ldi		YL, Curr1000
		st		Y, Temp					; store 1000er value
		clr		Temp
		ldi		YL, 100					; load 100 into YH:YL
 CONVINNER100:
		sub		ZL, YL					; Subtraction
		sbc		ZH, YH
		brcs	CONV100OUT				; exit if negative
		inc		Temp					; increase Result
		rjmp	CONVINNER100		
 CONV100OUT:
 		add		ZL, YL					; undo last Subtraction 8 Bit is enough
		clr		YH 						; Pointer to 100er value
		ldi		YL, Curr100
		st		Y, Temp					; store 100er value
		clr		Temp
		ldi		YL, 10					; load 10 into YL YH is not required
 CONVINNER10:
		sub		ZL, YL					; Subtraction 8 Bit are enough
		brcs	CONV10OUT				; exit if negative
		inc		Temp					; increase Result
		rjmp	CONVINNER10
 CONV10OUT:
 		add		ZL, YL					; undo last Subtraction 8 Bit is enough
		clr		YH 						; Pointer to 10er value
		ldi		YL, Curr10
		st		Y, Temp					; store 10er value
		clr		Temp
		ldi		YL, Curr1				; store 1er value
		st		Y, ZL
		pop		Temp
		pop		YL
		pop		YH
		pop		ZL
		pop		ZH
		ret

CALCFSOLL:							; Calculate the pfs value from received Drehzahl value
										; result is stored in DrehzHighSoll:DrehzLowSoll
		push	ZH
		push	ZL
		push	Temp
		clr		ZH
		ldi		ZL, 43					; multiplication value for 4,952MHz
		mov		Temp, Drehzahl			; multiply Drehzahl
		rcall	ATMUL
		sts		DrehzLowSoll, ZL		; store as goal value
		sts		DrehzHighSoll, ZH
		pop		Temp
		pop		ZL
		pop		ZH
		ret
CALCAMP:							; Calculate Amplitude from DrehzahlAkt value
										; result is stored in Amp
		push	Temp
		push	ZL
		push	ZH
		push	YL
		push	YH
		lds		ZL, DrehzLowAkt
		lds		ZH, DrehzHighAkt
		clr		YH
		ldi		YL, 10					; Divide Drehzahl by 10
		rcall	ATDIV
		clr		Temp
		cpse	ZH, Temp				; set Amp to ff if it is larger
		ser		ZL
		sts		Amp, ZL					; store Amplitude
		pop		YH
		pop		YL
		pop		ZH
		pop		ZL
		pop		Temp
		ret

;*****************************************************************************************
;	Multiplikation und Division
;*****************************************************************************************

ATMUL:								; Multiply two 8 bit values
										; put the values in ZL and Temp
										; result is stored in ZH:ZL
		push	YH
		push	YL
		push	Temp
		mov		YL, ZL
		clr		ZH
		clr		YH
		cpi		Temp, 0
		breq	RESZERO
 MULINNER:
 		dec		Temp
		breq	MULOUT
		add		ZL, YL
		adc		ZH, YH
		rjmp	MULINNER
 RESZERO:
 		clr		ZL
 MULOUT:
 		pop		Temp
		pop		YL
		pop		YH
		ret

ATDIV:								; Divide two values
										; put first value into ZH:ZL
										; and second value into YH:YL
										; result is stored in ZH:ZL
		push	XL
		push	XH
		push	Temp
		clr		Temp
		clr		XL
		clr		XH
 DIVINNER:
 		sub		ZL, YL
		sbc		ZH, YH
		brlo	DIVOUT
		adiw	XH:XL, 1
		rjmp	DIVINNER
 DIVOUT:
 		mov		ZH, XH
		mov		ZL, XL
 		pop		Temp
		pop		XH
		pop		XL
		ret

;*****************************************************************************************
;	Saa 828 Routinen
;*****************************************************************************************

SAA_INIT:							; send initialisation values to SAA
		cbi		SAA_CRTL_PORT, SAA_RSTN		; reset before init
		ldi		Saa_R0, 0b11111100		; pulse deletion time
		ldi		Saa_R1, 0b11000000		; carrier frequency and frequency range
		ldi		Saa_R2, 0b00111110		; pulse delay time
		ldi		Saa_Address, 0x04		; dummy register to write data as initialisation
		rcall	SAA_TRANSMIT			; initialise Saa
		ret

SAA_SEND_CONTROL:					; sends values stored in Drehzahl and Amplitude SRAM
									;	to SAA
;		rcall	SAA_INIT
		lds		Saa_R0, DrehzLowAkt		; load Drehzahl
		lds		Saa_R1, DrehzHighAkt
		andi	Saa_R1, 0b00001111		; normally not necessary 
		ori		Saa_R1, 0b00100000		; set inhibit bit
		lds		Saa_R2, Amp				; load Amplitude
		ldi		Saa_Address, 0x03		; mark as control data
		rcall	SAA_TRANSMIT			; send it
		ret

SAA_TRANSMIT:						; writes initialisation and control data to Saa
										; put the 24 bit contents into Saa_R0 to Saa_R2
										; if the data is control write 0x03 to Saa_Address
										; if it is initialisation write 0x04 to Saa_Address
		push	Temp
		clr		Temp
		out		SAA_PORTH, Temp			; write address R0 to SAA Bits 4..7
		out		SAA_PORTL, Temp			; write address R0 to SAA Bits 0..3
		sbi		SAA_CRTL_PORT, SAA_ALE
		cbi		SAA_CRTL_PORT, SAA_CSN
		cbi		SAA_CRTL_PORT, SAA_ALE
		mov		Temp, Saa_R0
		out		SAA_PORTH, Temp			; write data R0 to SAA Bits 4..7
		lsl		Temp
		out		SAA_PORTL, Temp			; write data R0 to SAA Bits 0..3
		cbi		SAA_CRTL_PORT, SAA_WRN
		sbi		SAA_CRTL_PORT, SAA_WRN
		ldi		Temp, 0x01
		out		SAA_PORTH, Temp			; write address R1 to SAA Bits 4..7
		lsl		Temp
		out		SAA_PORTL, Temp			; write address R1 to SAA Bits 0..3
		sbi		SAA_CRTL_PORT, SAA_ALE
		cbi		SAA_CRTL_PORT, SAA_ALE
		mov		Temp, Saa_R1
		out		SAA_PORTH, Temp			; write data R1 to SAA Bits 4..7
		lsl		Temp
		out		SAA_PORTL, Temp			; write data R1 to SAA Bits 0..3
		cbi		SAA_CRTL_PORT, SAA_WRN
		sbi		SAA_CRTL_PORT, SAA_WRN
		ldi		Temp, 0x02
		out		SAA_PORTH, Temp			; write address R2 to SAA Bits 4..7
		lsl		Temp
		out		SAA_PORTL, Temp			; write address R2 to SAA Bits 0..3
		sbi		SAA_CRTL_PORT, SAA_ALE
		cbi		SAA_CRTL_PORT, SAA_ALE
		mov		Temp, Saa_R2
		out		SAA_PORTH, Temp			; write data R2 to SAA Bits 4..7
		lsl		Temp
		out		SAA_PORTL, Temp			; write data R2 to SAA Bits 0..3
		cbi		SAA_CRTL_PORT, SAA_WRN
		sbi		SAA_CRTL_PORT, SAA_WRN
		mov		Temp, Saa_Address		; write address of a dummy register to SAA
		out		SAA_PORTH, Temp			; write data R1 to SAA Bits 4..7
		lsl		Temp
		out		SAA_PORTL, Temp			; write data R1 to SAA Bits 0..3
		sbi		SAA_CRTL_PORT, SAA_ALE
		cbi		SAA_CRTL_PORT, SAA_ALE
		ldi		Temp, 0x00
		out		SAA_PORTH, Temp			; write dummy data to SAA Bits 4..7
		lsl		Temp
		out		SAA_PORTL, Temp			; write dummy data to SAA Bits 0..3
		cbi		SAA_CRTL_PORT, SAA_WRN
		sbi		SAA_CRTL_PORT, SAA_WRN
		sbi		SAA_CRTL_PORT, SAA_CSN
		clr		Temp
		out		SAA_PORTL, Temp			; required for the ADC pullups
		pop		Temp
		sbi		SAA_CRTL_PORT, SAA_RSTN
		ret

DELAY775:	
		;ret						; *** provide 775 MZ delay including the rcall command
		push	DelayTemp				; 2MZ
		ldi		DelayTemp, 255			; 1MZ
 DELAYCOUNT:
 		dec		DelayTemp				; 1MZ
		brne	DELAYCOUNT				; 1/2MZ
		pop		DelayTemp				; 2MZ
		ret								; 3MZ, rcall 3MZ