;* Title	: Cryostat controller CTC-25N		
;* Version	: 1.00
;* Target	: ATmega8
;* Author	: wubblick@yahoo.com

.include "m8def.inc"

;***** Constantes:

.equ	Fclk	= 16000		;Fclk, kHz

;.equ	Baud	= 416		;2400 baud @ 16 MHz
.equ	Baud	= 207		;4800 baud @ 16 MHz
;.equ	Baud	= 103		;9600 baud @ 16 MHz

.equ	T2Div	= 124		;Timer 2: (16 MHz / 64) / 125 = 2 kHz
.equ	FltWn	= 56        	;digital filter window length 
.equ	PrCntV	= 20		;0.5 mS (2 kHz) * 20 = 10 mS
.equ	BeeD	= 255		;beep duration
.equ	ARCnV	= 16		;slow autorepeat count
.equ	ARDel	= 80		;initial autorepeat delay, x10 mS
.equ	ARSlw	= 18		;slow autorepeat rate,	   x10 mS
.equ	ARFst	= 6		;fast autorepeat rate,	   x10 mS
.equ	DebV	= 3		;debounce delay,	   x10 mS

;Serial link addresses:

.equ	Rx_Addr	= 0x00		;RX serial link address
.equ	Tx_Addr	= 0x00		;TX serial link address

;WAKE protocol constantes:

.equ	FEND	= 0xC0		;Frame END
.equ	FESC	= 0xDB		;Frame ESCape
.equ	TFEND	= 0xDC		;Transposed Frame END
.equ	TFESC	= 0xDD		;Transposed Frame ESCape

.equ	CRC_Init = 0xDE		;Initial CRC value
.equ	Fr_Max	= 16		;maximum frame length

;Errors codes:

.equ	Err_No	= 0x00		;no error
.equ	Err_Tx	= 0x01		;Rx/Tx error
.equ	Err_Bu	= 0x02		;device busy error
.equ	Err_Re	= 0x03		;device not ready error
.equ	Err_Pa	= 0x04		;parameters value error

;Commands codes:

.equ	C_Nop	= 0x00		;No operation
.equ	C_Err	= 0x01		;Get Error code
.equ	C_Echo	= 0x02		;Get Echo
.equ	C_Info	= 0x03		;Get Device Information
.equ	C_SetU	= 0x04		;Set U (heater voltage)
.equ	C_GetT	= 0x05		;Get Temperature
.equ	C_SetI	= 0x06		;Set Indication

;Keyboard scancodes:

.equ	K_NO	= 0x00		;no press
.equ	K_EX	= 0x01		;key EX scancode
.equ	K_DN	= 0x02		;key DN scancode
.equ	K_UP	= 0x04		;key UP scancode
.equ	K_EN	= 0x08		;key EN scancode

;***** Internal RAM variables:

.DSEG	;data segment (internal SRAM)

Rx_Str:	.byte 22		;RX thread data structure
Tx_Str:	.byte 22		;TX thread data structure

.equ	x_Ptr	= 0		;data pointer
.equ	x_Add	= 1		;address
.equ	x_Cmd	= 2		;command
.equ	x_Nbt	= 3		;number of bytes
.equ	x_Dat	= 4		;data
.equ	x_Crc	= 20		;CRC
.equ	x_Pre	= 21		;pre-byte

.equ	Rx_Dat	= Rx_Str+x_Dat	;RX data
.equ	Tx_Dat	= Tx_Str+x_Dat	;TX data

KBD:	.byte 5			;keyboard data structure

.equ	Lc	= 0		;LastCode offset
.equ	Tc	= 1		;TempCode offset
.equ	ARCnt	= 2		;autorepeat counter offset
.equ	DebTM	= 3		;debounce timer offset
.equ	KeyTM	= 4		;key timer offset

ADCm:	.byte 2			;ADC code

;***** Internal EEPROM variables:

.ESEG	;EEPROM segment

Enone:	.byte 1			;address 0 not used

;***** Global Register Variables:

.def	tsreg	= r2		;* SREG store
.def	AdcLr	= r3		;* ADC code low
.def	AdcHr	= r4		;* ADC code high	

.def	temp	= r16		;Temporary register temp
.def	Cnt	= r17		;Temporary register Cnt
.def	tempL	= r18		;Temporary register tempL
.def	tempH	= r19		;Temporary register tempH
.def	PrCnt	= r20		;* Program counter (10 mS)
.def	CMD	= r21		;command code
.def	SndD	= r22		;sound duration
.def	DigF	= r23		;* digital filter index

.def	FLAGS	= r24		;Flags:
.equ	NEWPR	= 0		;New Press flag

;r28,r29 used as Y-register
;r30,r31 used as Z-register

;* - used in timer interrupt

;***** Port Definitions

.equ	PB	= PORTB		;Port B
.equ	DIRB	= 0b11111111	;Port B direction
.equ	PUPB	= 0b00000101	;Port B pull-ups
.equ	SHDN	= PB0		;Shut Down
.equ	PWM	= PB1		;PWM Out
.equ	LOAD	= PB2		;Serial Bus LOAD
.equ	SDATA	= PB3		;Serial Bus DATA
.equ	MISO	= PB4
.equ	SCLK	= PB5		;Serial Bus CLOCK

.equ	PC	= PORTC		;Port C
.equ	DIRC	= 0b11000010	;Port C direction
.equ	PUPC	= 0b00111100	;Port C pull-ups
.equ	ADCIN	= PC0		;ADC input
.equ	PortC1	= PC1
.equ	KEX	= PC2		;Key EX
.equ	KDN	= PC3		;Key DN
.equ	KUP	= PC4		;Key UP
.equ	KEN	= PC5		;Key EN

.equ	PD	= PORTD		;Port D
.equ	DIRD	= 0b11111110	;Port D direction
.equ	PUPD	= 0b10000101	;Port D pull-ups
.equ	RXD	= PD0		;UART RXD
.equ	TXD	= PD1		;UART TXD
.equ	LedH	= PD2		;LED HEATER
.equ	PortD3	= PD3
.equ	PortD4	= PD4
.equ	PortD5	= PD5
.equ	SND	= PD6		;Sound Out
.equ	OE	= PD7		;Serial registers OE

;***** Macros

.macro	clbr			;clear bit in register
	cbr @0,exp2(@1)
.endm

.macro	stbr			;set bit in register
	sbr @0,exp2(@1)
.endm

.macro	bbrc			;branch if bit in register clear
	sbrs @0,@1
	rjmp @2
.endm

.macro	bbrs			;branch if bit in register set
	sbrc @0,@1
	rjmp @2
.endm

.macro	bbic			;branch if bit in I/O clear
	sbis @0,@1
	rjmp @2
.endm

.macro	bbis			;branch if bit in I/O set
	sbic @0,@1
	rjmp @2
.endm

.macro	ldx			;load XL, XH with word
	ldi XL,byte1(@0)
	ldi XH,byte2(@0)
.endm

.macro	ldy			;load YL, YH with word
	ldi YL,byte1(@0)
	ldi YH,byte2(@0)
.endm

.macro	ldz			;load ZL, ZH with word
	ldi ZL,byte1(@0)
	ldi ZH,byte2(@0)
.endm

.macro	stdi			;store immediate indirect with displacement
	ldi temp,@1
	std @0,temp
.endm

;***** Interrupt Vectors

.CSEG	;Code segment

.org	0
	rjmp	INIT		;Reset Handle

.org	OC2addr
	rjmp	Timer		;Timer Handle

.org	URXCaddr
	rjmp	UARTRX		;UART Receive Complete Interrupt Handle

.org	UTXCaddr
	rjmp	UARTTX		;UART Transmit Complete Interrupt Handle

;***** Program Execution Starts Here

INIT:	wdr			;watchdog reset
	ldi	temp,0b00011011	
	out	WDTCR,temp	;WDCE, WDE, 140 mS

	ldi	temp,low (RAMEND) ;Locate stack
	out	SPL,temp
	ldi	temp,high(RAMEND)
	out	SPH,temp

;Ports setup:
	
	ldi	temp,PUPB
	out	PORTB,temp	;Init PORTB and on/off pullup
	ldi	temp,DIRB	
	out	DDRB,temp	;Set PORTB direction
	
	ldi	temp,PUPC
	out	PORTC,temp	;Init PORTC and on/off pullup
	ldi	temp,DIRC
	out	DDRC,temp	;Set PORTC direction

	ldi	temp,PUPD
	out	PORTD,temp	;Init PORTD and on/off pullup
	ldi	temp,DIRD
	out	DDRD,temp	;Set PORTD direction

;Variables init:

	ldi	Cnt,16
	ldz	Rx_Dat
	clr	temp
ClrBf:	st	Z+,temp		;clear RX data buffer
	dec	Cnt
	brne	ClrBf

	ldy	Rx_Str
	ldz	Tx_Str
	stdi	Y+x_Add,Rx_Addr	;init RX address
	stdi	Y+x_Ptr,0	;init RX pointer
	stdi	Z+x_Add,Tx_Addr	;init TX address
	stdi	Z+x_Ptr,0	;init TX pointer

	ldi	CMD,C_Nop	;command clear

	clr	temp
	sts	ADCm+0,temp	;clear ADC code
	sts	ADCm+1,temp
	clr	AdcLr
	clr	AdcHr
	ldi	DigF,FltWn	;DigF init


;Periphery setup:

;ADC:

	ldi	temp,0b11000000
	out	ADMUX,temp	;int REF, ADC0
	ldi	temp,0b10000111
	out	ADCSR,temp	;ADEN, CK/128

;USART:

	ldi	temp,byte1(Baud)
	out	UBRRL,temp
	ldi	temp,byte2(Baud)
	out	UBRRH,temp	;set baud rate

	ldi	temp,0b11011000
	out	UCSRB,temp	;RXCIE, TXCIE, RXEN, TXEN
	ldi	temp,0b10000110
	out	UCSRC,temp	;URSEL, UCSZ1, UCSZ0

;Timer 1:

	ldi	temp,0b10000011
	out	TCCR1A,temp	;OC1A - fast 10-bit PWM
	ldi	temp,0b00001001	
	out	TCCR1B,temp	;fast 10-bit PWM, CK/1

;Timer 2:

	ldi	temp,0b00001100	
	out	TCCR2,temp	;CTC, CK/64
	ldi	temp,T2Div
	out	OCR2,temp	;OCR2 = 124 (CK/64/125)

	ldi	temp,0b10000000	
	out	TIFR,temp	;Clear pending interrupts
	out	TIMSK,temp	;OCIE2

;Subsystems init:
	
	rcall	Set_U		;U reset
	rcall	Set_I		;Indicate "0000"
	cbi	PORTD,OE	;enable registers
	sei			;Interrupts enable
	rcall	Beep		;beep

;Main loop	
	
MAIN:	rcall	SCAN		;scan keyboard
	ldi	YL,KBD		;keyboard data structure base
	ldd	tempL,Y+Lc	
	cp	tempL,temp	;scancode = LastCode ?
	breq	Hold		;branch if same key
	
	ldd	tempL,Y+Tc
	cp	tempL,temp	;scancode = TempCode ?
	brne	NewP		;branch if new key
	
	ldd	tempL,Y+DebTM
	tst	tempL		;check debounce timer
	brne	Hold
	
	std	Y+Lc,temp	;LastCode <- scancode
	stbr	FLAGS,NEWPR	;set new press flag
	ldi	tempH,ARDel	;autorepeat delay value
	ldi	tempL,ARCnV
	rjmp	Stac		;go to store ARCnt
	
NewP:	std	Y+Tc,temp	;TempCode <- scancode
	ldi	tempL,DebV
	std	Y+DebTM,tempL	;debounce timer load
	
Hold:	clbr	FLAGS,NEWPR	;clear new press flag
	ldd	tempL,Y+KeyTM
	tst	tempL		;check key timer
	brne	Proc
	stbr	FLAGS,NEWPR	;set new press flag
	ldd	tempL,Y+ARCnt
	tst	tempL
	breq	Fast		;fast autorepeat if count is over
	dec	tempL		;dec autorepeat counter
	ldi	tempH,ARSlw	;slow autorepeat rate
	
Stac:	std	Y+ARCnt,tempL	;store autorepeat counter ARCnt
	rjmp	Stkt		;go to store KeyTM
	
Fast:	ldi	tempH,ARFst	;fast autorepeat rate
Stkt:	std	Y+KeyTM,tempH	;store key timer KeyTM
Proc:
	
;KBD+Lc	= true scancode,
;NEWPR	= 1 if new press

;Process key functions:
	
	bbrc	FLAGS,NEWPR,NoPr
	ldd	temp,Y+Lc	;temp <- LastCode
	cpi	temp,K_NO
	breq	NoPr		;skip if no press
	rcall	Beep		;beep
	cpi	temp,K_UP
	brne	Pro1
	rcall	Do_UP		;key UP processing
Pro1:	cpi	temp,K_DN
	brne	Pro2
	rcall	Do_DN		;key DN processing
Pro2:	cpi	temp,K_EX
	brne	Pro3
	rcall	Do_EX		;key EX processing
Pro3:	cpi	temp,K_EN
	brne	NoPr
	rcall	Do_EN		;key EN procesing
NoPr:

;Process commands:

	cpi	CMD,C_Err
	brne	Cmd1
	rcall	Do_Err
Cmd1:	cpi	CMD,C_Echo
	brne	Cmd2
	rcall	Do_Echo
Cmd2:	cpi	CMD,C_Info
	brne	Cmd3
	rcall	Do_Info
Cmd3:	cpi	CMD,C_SetU
	brne	Cmd4
	rcall	Do_SetU
Cmd4:	cpi	CMD,C_GetT
	brne	Cmd5
	rcall	Do_GetT
Cmd5:	cpi	CMD,C_SetI
	brne	Cmd6
	rcall	Do_SetI
Cmd6:

;Watchdog restart:

	wdr
	rjmp 	MAIN		;Main loop

;***** Subroutines area:

;***** Keyboard functions processing:

;Key "UP" processing:

Do_UP:	ret
	
;Key "DN" processing:
		
Do_DN:	ret
	
;Key "EX" processing:

Do_EX:	ret
	
;Key "EN" processing:
		
Do_EN:	ret

;***** End of keyboard functions processing.

;***** PC commands processing:

;Command "C_Err" processing:
;Send error message:

Do_Err:
	ldz	Tx_Str
	std	Z+x_Cmd,CMD	;command
	stdi	Z+x_Nbt,1	;N
	stdi	Z+x_Dat,Err_Tx	;error code
	rcall	UARTTX		;begin frame TX
	ldi	CMD,C_Nop	;end of command
	ret

;Command "C_Echo" processing:
;Send echo:

Do_Echo:
	ldy	Rx_Str
	ldz	Tx_Str
	std	Z+x_Cmd,CMD	;command
	ldd	temp,Y+x_Nbt
	std	Z+x_Nbt,temp	;copy N
	tst	temp
	breq	Ech_No
	mov	Cnt,temp
	ldy	Rx_Dat
	ldz	Tx_Dat
Ech_C:	ld	temp,Y+
	st	Z+,temp
	dec	Cnt
	brne	Ech_C
Ech_No:	rcall	UARTTX		;begin frame TX
	ldi	CMD,C_Nop	;end of command
	ret

;Command "C_Info" processing:

Do_info:
	ldz	Tx_Str
	std	Z+x_Cmd,CMD	;command
	ldi	Cnt,16
	std	Z+x_Nbt,Cnt	;copy N
	ldy	Tx_Dat
	ldi	ZL,byte1(Info*2)
	ldi	ZH,byte2(Info*2)
Inf_C:	lpm	temp,Z+
	st	Y+,temp
	dec	Cnt
	brne	inf_C
	rcall	UARTTX		;begin frame TX
	ldi	CMD,C_Nop	;end of command
 	ret
	
;Command "C_SetU" processing:

Do_SetU: 
	rcall	Set_U
	ldz	Tx_Str
	std	Z+x_Cmd,CMD	;command
	stdi	Z+x_Nbt,1	;N
	stdi	Z+x_Dat,Err_No	;error code
	brtc	U_ne
	stdi	Z+x_Dat,Err_Pa	;error code
U_ne:	rcall	UARTTX		;begin frame TX
	ldi	CMD,C_Nop	;end of command
	ret

;Command "C_GetT" processing:

Do_GetT:
	rcall	Get_T
	ldz	Tx_Str
	std	Z+x_Cmd,CMD	;command
	stdi	Z+x_Nbt,3	;N
	stdi	Z+x_Dat,Err_No	;error code
	rcall	UARTTX		;begin frame TX
	ldi	CMD,C_Nop	;end of command
	ret

;Command "C_SetI" processing:

Do_SetI:
	rcall	Set_I
	ldz	Tx_Str
	std	Z+x_Cmd,CMD	;command
	stdi	Z+x_Nbt,1	;N
	stdi	Z+x_Dat,Err_No	;error code
	brtc	I_ne
	stdi	Z+x_Dat,Err_Pa	;error code
I_ne:	rcall	UARTTX		;begin frame TX
	ldi	CMD,C_Nop	;end of command
	ret

;***** End of PC commands processing.

;Beep generation:

Beep:	ldi	SndD,BeeD
	ret
	
;Scan local keyboard:
;Out: temp - scancode

SCAN:	clr	temp
	clc			;C <- 0
	sbis	PINC,KEX
	sec
	rol	temp		;C <- temp.7..temp.0 <- C
	sbis	PINC,KDN
	sec
	rol	temp		;C <- temp.7..temp.0 <- C
	sbis	PINC,KUP
	sec
	rol	temp		;C <- temp.7..temp.0 <- C
	sbis	PINC,KEN
	sec
	rol	temp		;C <- temp.7..temp.0 <- C
	ret

;Set U (heater voltage):
;Rx_Dat[0] - U low
;Rx_Dat[1] - U high
;T=1 - parameters value error

Set_U:	clt
	ldy	Rx_Dat
	ldd	tempH,Y+1
	ldd	tempL,Y+0
	cpi	tempH,0x04	;if (U > 0x3FF)
	brlo	U_Ld
	set
	ldi	tempH,0x03	; U = 0x3FF
	ldi	tempL,0xFF
U_Ld:	out	OCR1AH,tempH	
	out	OCR1AL,tempL
	or	tempH,tempL
	brne	U_on
U_off:	sbi	PORTB,SHDN	;shut down on
	sbi	PORTD,LedH	;LED HEATER off
	ret
U_on:	cbi	PORTB,SHDN	;shut down off
	cbi	PORTD,LedH	;LED HEATER on
	ret

;Get Temperature:
;Tx_Dat[1] - T low
;Tx_Dat[2] - T high

Get_T:	ldz	Tx_Dat
	lds	temp,ADCm+0
	std	Z+1,temp
	lds	temp,ADCm+1
	std	Z+2,temp
	ret

;Set Indication:
;Rx_Dat[0] - digit 1
;Rx_Dat[1] - digit 2
;Rx_Dat[2] - digit 3
;Rx_Dat[3] - digit 4
;Rx_Dat[4] - points
;T=1 - parameters value error

Set_I:	clt
	ldy	Rx_Dat+4
	ld	tempL,Y		;read Points

	ldi	Cnt,4
	ldy	Rx_Dat
	cbi	PORTB,LOAD	;LOAD <- 0
N_Ch:	ld	tempH,Y+
	brmi	Dir_S		;direct segment control

	cpi	tempH,13
	brlo	Dg_t
	set
	ldi	tempH,12
Dg_t:	ldi	ZL,low (FONT*2)	;table base
	ldi	ZH,high(FONT*2)
	add	ZL,tempH	;add offset (Z + tempH)
	clr	r0
	adc	ZH,r0
	lpm	temp,Z		;read byte from table
	rjmp	St_Pt

Dir_S:	clr	temp
	bst	tempH,0		;reorder bits
	bld	temp,7
	bst	tempH,1
	bld	temp,0
	bst	tempH,2
	bld	temp,2
	bst	tempH,3
	bld	temp,4
	bst	tempH,4
	bld	temp,5
	bst	tempH,5
	bld	temp,6
	bst	tempH,6
	bld	temp,1

St_Pt:	ror	tempL
	brcc	Wr_Ch		;check Point bit
	stbr	temp,H		;Point on
Wr_Ch:	rcall	SPI_Wr
	dec	Cnt
	brne	N_Ch
	sbi	PORTB,LOAD	;LOAD <- 1
	ret

;Write byte via SPI:
;temp -> SPI

SPI_Wr:	push	Cnt
	ldi	Cnt,8		;counter load
wr_bit:	cbi	PORTB,SCLK	;CLK=0
	rol	temp
	brcs	wr_1
wr_0:	cbi	PORTB,SDATA	;DATA=0 or..
	rjmp	wr_clk
wr_1:	sbi	PORTB,SDATA	;DATA=1
wr_clk:	dec	Cnt
	sbi	PORTB,SCLK	;CLK=1
	brne	wr_bit
	sbi	PORTB,SDATA	;DATA=1
	pop	Cnt
	ret

;***** Timer 2 output compare interrupt
;500 uS interrupt: sound generation,
;software timers and ADC filtering

Timer:	in	tsreg,SREG	;save status register
	push	ZL
	push	ZH
	push	temp

	dec	DigF		;DigF = DigF - 1
	clr	temp
	ldi	ZL,byte1(FltWin*2)
	ldi	ZH,byte2(FltWin*2)
	add	ZL,DigF
	adc	ZH,temp		;add table offset
	lpm	temp,Z
	tst	temp
	breq	No_Smp

	in	ZL,ADCL		;read ADC code
	in	ZH,ADCH
	sbi	ADCSR,ADSC	;start ADC
                    
	add	AdcLr,ZL
	adc	AdcHr,ZH

No_Smp:	tst	DigF		;check DigF
	brne	F_rdy
	ldi	DigF,FltWn	;new cycle
	rjmp	Snd_g
F_rdy:	cpi	DigF,FltWn-1	
	brne	Snd_g
	sts	ADCm+0,AdcLr	;store ADC value
	sts	ADCm+1,AdcHr
	clr	AdcLr
	clr	AdcHr

Snd_g:	tst	SndD		;sound generation
	breq	Snd_no
	dec	SndD
	sbrc	SndD,0
	sbi	PORTD,SND
	sbrs	SndD,0
Snd_no:	cbi	PORTD,SND

	dec	PrCnt		;dec program counter
	brne	T_ret
	ldi	PrCnt,PrCntV	;program counter reload

	lds	temp,KBD+DebTM	;10 mS counter DebTM:
	tst	temp
	breq	Tm1
	dec	temp
	sts	KBD+DebTM,temp

Tm1:	lds	temp,KBD+KeyTM	;10 mS counter KeyTM:
	tst	temp
	breq	T_ret
	dec	temp
	sts	KBD+KeyTM,temp

T_ret:	pop	temp
	pop	ZH
	pop	ZL
	out	SREG,tsreg	;restore status register
	reti

;***** UART receive complete interrupt

UARTRX:	push	temp
	in	temp,SREG	;save status register
	push	temp
	in	temp,UDR	;get byte
	sei			;enable nested interrupts
	push	Cnt
	push	ZL
	push	ZH

	ldz	Rx_Str		;load RX data structure address

	cpi	temp,FEND
	brne	RX0
	std	Z+x_Pre,temp	;Rx_Pre = byte
	push	temp
	ldi	temp,CRC_Init
	std	Z+x_Crc,temp	;CRC init
	clr	temp
	std	Z+x_Ptr,temp	;pointer init
	pop	temp
	rjmp	RX12

RX0:	ldd	Cnt,Z+x_Ptr
	cpi	Cnt,0		;if (Rx_Ptr == 0)
	brne	RX1
	rjmp	RX_RET		; Wait for FEND

RX1:	push	Cnt
	ldd	Cnt,Z+x_Pre	;load pre-byte
	std	Z+x_Pre,temp	;save new pre-byte
	mov	temp,Cnt
	pop	Cnt
	cpi	temp,FESC	;if (Rx_Pre == FESC)
	brne	RX3
	ldd	temp,Z+x_Pre
	cpi	temp,TFESC	; if (byte == TFESC)
	brne	RX2
	ldi	temp,FESC	;  byte = FESC
	rjmp	RX5
RX2:	cpi	temp,TFEND	;  else if (byte == TFEND)
	brne	RX_Err		;        byte = FEND
	ldi	temp,FEND	;	 else Rx_Err
	rjmp	RX5			

RX3:	ldd	temp,Z+x_Pre
	cpi	temp,FEND	;if (byte == FEND)
	breq	Rx_Err		; Rx_Err
RX4:	cpi	temp,FESC	;if (byte == FESC)
	breq	RX_RET		; skip

RX5:	cpi	Cnt,1		;if (Rx_Ptr == 1)
	brne	RX7
	sbrc	temp,7		; if (byte.7 == 0)
	rjmp	RX6
	inc	Cnt		;  { inc R0
	std	Z+x_Ptr,Cnt	;    inc Rx_Ptr
	rjmp	RX11		;    save Tx_Cmd }
RX6:	clbr	temp,7
	tst	temp		; else if (!Addr) broadcast
	breq	RX12
	push	Cnt
	ldd	Cnt,Z+x_Add
	cp	temp,Cnt	;  else if (Addr == Rx_Add)
	pop	Cnt
	brne	RX10
	rjmp	RX12		;   match else new_frame

RX7:	cpi	Cnt,2		;if (Rx_Ptr == 2)
	brne	RX8
	sbrc	temp,7		; if (byte.7 == 1)
	rjmp	Rx_Err		;  Rx_Err
	rjmp	RX11		;  else save Tx_Cmd	

RX8:	cpi	Cnt,3		;if (Rx_Ptr == 3)
	brne	RX9
	cpi	temp,Fr_Max+1	; if (Tx_Nbt < Fr_Max+1)
	brlo	RX11		;  save Tx_Nbt
	ldi	temp,Fr_Max	;  else { Tx_Nbt = Fr_Max
	rjmp	RX11		;         save Tx_Nbt }

RX9:	push	temp
	ldd	temp,Z+x_Nbt
	subi	temp,-3
	cp	temp,Cnt	;if (Rx_Ptr <= Rx_Nbt+3)
	pop	temp
	brsh	RX11		; save Tx_Dat[] else

	ldd	Cnt,Z+x_Crc
	cp	temp,Cnt	;  if (byte != Rx_Crc) 
	brne	Rx_Err		;   Rx_Err
	ldd	CMD,Z+x_Cmd	;   else { CMD = Rx_Cmd
RX10:	clr	temp
	std	Z+x_Ptr,temp	;          Rx_Ptr = 0 }
	rjmp	RX_RET

Rx_Err:	ldi	CMD,C_Err	;CMD = C_Err
	rjmp	RX10

RX11:	add	ZL,Cnt
	clr	Cnt
	adc	ZH,Cnt
	st	Z,temp		;save byte
	ldz	Rx_Str		;restore RX data structure address
RX12:	rcall	Do_Crc		;update CRC
	ldd	Cnt,Z+x_Ptr
	inc	Cnt
	std	Z+x_Ptr,Cnt	;inc Rx_Ptr

RX_RET:	pop	ZH
	pop	ZL
	pop	Cnt
	pop	temp
	out	SREG,temp	;restore status register
	pop	temp
	reti

;***** UART transmit complete interrupt

UARTTX:	sei			;enable nested interrupts
	push	temp
	in	temp,SREG	;save status register
	push	temp
	push	Cnt
	push	ZL
	push	ZH

	ldz	Tx_Str		;load TX data structure address

	ldd	Cnt, Z+x_Ptr
	ldd	temp,Z+x_Nbt
	subi	temp,-4
	cp	temp,Cnt	;if (Tx_Ptr > Tx_Nbt+4)
	brsh	TX0
	clr	temp
	std	Z+x_Ptr,temp
	rjmp	TX_RET		; TX complete

TX0:	brne	TX1		;if (Tx_Ptr == Tx_Nbt+4)
	ldd	temp,Z+x_Crc	; byte = Tx_Crc
	rjmp	TX4

TX1:	cpi	Cnt,0		;if (Tx_Ptr == 0)
	brne	TX2
	ldi	temp,CRC_Init	; { CRC init
	std	Z+x_Crc,temp
	ldi	temp,FEND	;   byte = FEND
	std	Z+x_Pre,temp	;   save pre-byte }
	rjmp	TX4

TX2:	cpi	Cnt,1		;if (Tx_Ptr == 1)
	brne	TX3
	ldd	temp,Z+x_Add	; { byte = Tx_Add
	tst	temp
	brne	TX4		;   if (Tx_Add == 0)
	inc	Cnt		;   inc Cnt
	std	Z+x_ptr,Cnt	;   inc Tx_Ptr }

TX3:	clr	temp
	add	ZL,Cnt
	adc	ZH,temp
	ld	temp,Z		;get byte
	ldz	Tx_Str		;restore TX data structure address
TX4:	push	temp		;save uncoded byte

	cpi	Cnt,1		;if (Tx_Ptr == 1)
	brne	TX5
	ori	temp,0x80	; byte = byte | 0x80

TX5:	cpi	Cnt,0		;if (Tx_Ptr == 0)
	breq	TX9		; skip stuffing

TX6:	cpi	temp,FEND	;if (byte == FEND)
	brne	TX7
	ldd	temp,Z+x_Pre
	cpi	temp,FESC	; if (Tx_Pre == FESC)
	brne	TX8
	ldi	temp,TFEND	;  byte = TFEND
	rjmp	TX9		;   else...

TX7:	cpi	temp,FESC	;else if (byte == FESC)
	brne	TX9
	ldd	temp,Z+x_Pre
	cpi	temp,FESC	; if (Tx_Pre == FESC)
	brne	TX8
	ldi	temp,TFESC	;  byte = TFESC
	rjmp	TX9

TX8:	ldi	temp,FESC	;   else { byte = FESC
	out	UDR,temp	;          TX byte
	std	Z+x_Pre,temp	;          save pre-byte }
	pop	temp		;stack balance
	rjmp	TX_RET

TX9:	out	UDR,temp	;TX byte
	std	Z+x_Pre,temp	;save pre-byte
	pop	temp		;get uncoded byte
	rcall	Do_CRC		;update CRC
	ldd	Cnt,Z+x_Ptr
	inc	Cnt
	std	Z+x_Ptr,Cnt	;inc Tx_Ptr

TX_RET:	pop	ZH
	pop	ZL
	pop	Cnt
	pop	temp
	out	SREG,temp	;restore status register
	pop	temp
	reti

;Update CRC:
;Input: temp - data byte
;Z - pointer to Rx_Str or Tx_Str
;Out: [Z+x_Crc] - new CRC value

Do_CRC:	push	YL
	push	YH
	ldi	Cnt,8        	;Set to shift eight bits
	ldd	YH,Z+x_Crc
	
CRCL:	mov	YL,temp
	eor	YL,YH    	;Calculate DQIN xor CRCT0
	ror	YL          	;Move it to the carry
	brcc	ZERO        	;Skip if DQIN xor CRCT0 = 0
	ldi	YL,0x18
	eor	YH,YL    	;Update the CRC value
ZERO:	ror	YH          	;Position the new CRC
	lsr	temp           	;Position next bit in LSB
	dec	Cnt
	brne	CRCL		;Repeat for eight bits
	std	Z+x_Crc,YH
	pop	YH
	pop	YL
	ret              	;Return

;Device Info:

Info:	.DB "CTC-25N V1.0 001"

;Font table:

FONT:	     ;AFEDHCGB    AFEDHCGB
	.DB 0b11110101, 0b00000101	;0, 1
	.DB 0b10110011, 0b10010111	;2, 3
	.DB 0b01000111, 0b11010110	;4, 5
	.DB 0b11110110, 0b10000101	;6, 7
	.DB 0b11110111, 0b11010111	;8, 9
	.DB 0b00000010, 0b00000000	;-, blank

.equ	H =3				;point

;Characters codes table:

.equ	chMIN	=0x0A	;character "-" code
.equ	ch_	=0x0B	;character "blank" code

;Digital filter window:

FltWin:	.DB 1, 1, 1, 1, 0, 0, 0, 0
	.DB 1, 1, 1, 1, 0, 0, 0, 0
	.DB 1, 1, 1, 1, 1, 1, 1, 1
	.DB 1, 1, 1, 1, 1, 1, 1, 1
	.DB 1, 1, 1, 1, 1, 1, 1, 1
	.DB 0, 0, 0, 0, 1, 1, 1, 1
	.DB 0, 0, 0, 0, 1, 1, 1, 1

