;****************************************************************
;                  	E-key
;         a Dallas IButton (tm) based Electronic key
;****************************************************************
;
;Programmer :  Philippe GROS
;Date       :  01 Sept 1999
;Version    :  V1.7
;
.DEVICE	AT90S1200	;set processor type

.NOLIST
.include "1200def.inc"
.LIST
.include "const.asm"

;****************************************************************
;*
;*   Program starts here
;*
;****************************************************************
.cseg
.org	0x0000
rjmp	start		;Reset Handler

;.org	INT0addr
rjmp	ext_int0	;External Interrupt Handler


;****************************************************************
start:
	ldi	r_CD,0b10000000		;disable analog comparator
	out	ACSR,r_CD
	  ;pb0,pb1=out AND high level (1)
	  ;pb2    =out AND high level (LED OFF)
	  ;pd0=out(TxD), pd1=input(RxD)
	  ;pd2=in AND High level (activate internal pullup)
	ldi	r_CD,0b00000111		;Port B 0=input, 1=output
	out	DDRB,r_CD
	ldi	r_CD,0b00000011		;inactive relays (0,1), led ON(2)
	out	PORTB,r_CD
	ldi	r_CD,0b00000001		;PD0=out(txd),PD1=in(rxd)
	out	DDRD,r_CD
	ldi	r_CD,0b00000101		;
	out	PORTD,r_CD
	ldi	rFlags,0		;init rFlags to 0
CheckNVR:       ;Check nvram consistency
	ldi	pointer,$00	;start from 00
	mov	CalcCRC,pointer	;init CRC to 0
	ldi	r_BytCntr,64	;64 bytes to check
Chk01:	out	EEAR,pointer	;place address to the register
	rcall	RDeeprom
	in	r_CD,EEDR	;load register to r_CD
	rcall	Do_CRC		;compute CRC
	inc	pointer		;next nvram location
	dec	r_BytCntr	;all bytes checked?
	brne	Chk01		;no, branch
	tst	CalcCRC		;valid result is 00
	breq	InitINT		;Checksum OK, continue
		;a nvram checksum error has occured
	sbr	rFlags,CsumER	;set flag CsumER (bit 1)
     ;Init WatchDog
	ldi	r_CD,0b00001111	;Enable WD and set prescaler to 2048000cycles
	out	WDTCR,r_CD	;To Watchdog Timer control register
     ;Interrupt initializing
InitINT:
        ldi	r_CD,0b00000010
        out	MCUCR,r_CD	;falling edge trigger on INT0
        ldi	r_CD,$40
        out	GIMSK,r_CD	;enable interrupt INT0
	sei			;enable interrupts
	rjmp	CRLF		;print signon and wait for command

idle:	;Here we are waiting for the IB Presence detect, or receiving 
	;character from the serial interface. 
	clr	mail
iidle:
	wdr			;Watchdog Reset
	sbrc	rFlags,$01	;skip if NVram Checksum OK
	sbi	PORTB,$02	;PB2 set to 1 (Turn the LED: OFF)
	tst	mail		;check if character received from serial line
	breq	iidle
	clr	mail		;YES a char has been received
	mov	sbuf,rbuf	;echo it
	rcall	out_char	;to txd
	cpi	rbuf,$41	;'A'
	brne	TstC
	rjmp	Acmd
TstC:	cpi	rbuf,$43	;'C'
	brne	TstD
	rjmp	Ccmd
TstD:	cpi	rbuf, $44	;'D'
	brne	TstS
	rjmp	Dcmd
TstS:	cpi	rbuf,$53	;'S'
	brne	TstZ
	rjmp	Scmd
TstZ:	cpi	rbuf,$5A	;'Z'
	brne	TstErr
	rjmp	Zcmd
TstErr:				;not a valid command, ignore
	rjmp	CmdErr		;send * with CR and LF

;****************************************************************
;       Command processing routines
	;AnXXYY	n=key number, XX=action relay 1, YY=action relay 2
	;       XX=00 no action, increment: 1 second (approx)      *
	;Cn     Clear key number "n"                               * 
	;D	Dump: print out all the eeprom informations
	;	the seven serial key number, with action relays
	;Sn	Acquire a serial number to key "n"                 *
	;Z	Zero all counters, SN keys.                        *
        ; * = This command update the eeprom CRC

;----------------------------------------------------------------
	;AnXXYY	n=key number, XX=action relay 1, YY=action relay 2
	;       XX=00 no action, increment: 1 second
Acmd:	rcall	fetchca		;wait for a char from input
	andi	rbuf,$07	;1 to 7 valid
	tst	rbuf
	brne	Acmd1
	rjmp	CmdErr		;Error, drop command
Acmd1:	dec	rbuf		;use 0 to 6
	mov	pointer,rbuf	;key number
	lsl	pointer		;compute eeprom address
	lsl	pointer
	lsl	pointer
	ldi	offset,$06	;load offset
	add	pointer,offset	;location for xx relay timing
	ldi	r_Zreg,1	;SN starts at the address 1
	rcall	fetchhex	;get relay1 hex value 
	out	EEDR,hex_buf	;to eeprom data register
	out	EEAR,pointer	;address in eeprom
	rcall	WReeprom
	inc	pointer
	rcall	fetchhex
	out	EEDR,hex_buf	;to eeprom data register
	out	EEAR,pointer	;address in eeprom
	rcall	WReeprom
	rjmp	UpdEEcrc	;OK, update CRC and terminate
;----------------------------------------------------------------
	;Cn     Clear key number "n"
Ccmd:	rcall	fetchca		;wait for a char from input
	andi	rbuf,$07	;1 to 7 valid
	tst	rbuf
	brne	Ccmd1
	rjmp	CmdErr		;Error, drop command
Ccmd1:	dec	rbuf		;use 0 to 6
	mov	pointer,rbuf	;key number
	lsl	pointer		;compute eeprom address
	lsl	pointer
	lsl	pointer
	ldi	r_Zreg,1	;SN starts at the address 1
	ldi	r_BytCntr,0	;initialize counter to 00
Ccmd2:	ldi	hex_buf,$55	;Clear with value 55
	out	EEDR,hex_buf	;to eeprom data register
	out	EEAR,pointer	;address in eeprom
	rcall	WReeprom
	inc	pointer
	inc	r_BytCntr
	cpi	r_BytCntr,$8	;stop after 8 bytes
	brne	Ccmd2
	rjmp	UpdEEcrc	;OK, update CRC and terminate
;----------------------------------------------------------------
	;D	Dump: print out all the eeprom informations
	;	the seven serial key number, with action relays
	;	all the error counters
Dcmd:
        ldi	sbuf,13		;send CRLF
        rcall	out_char
        ldi	sbuf,10
        rcall	out_char
        ldi	r_BytCntr,0	;initialize pointer to 00
DCloop:	mov	Scratch1,r_BytCntr
        andi	Scratch1,$7	
        brne	DCnocr
        ldi	sbuf,13		;CRLF after 8 eeprom values
        rcall	out_char
        ldi	sbuf,10
        rcall	out_char
DCnocr:
	out	EEAR,r_BytCntr	;place address to the register
	rcall	RDeeprom
	in	hex_buf,EEDR	;load register to hex_buf
	rcall	out_hex1	;and output both nibbles
	rcall	out_hex0
	ldi	sbuf,$20	;space as separator
	rcall	out_char
	inc	r_BytCntr
	cpi	r_BytCntr,56	;stop after 7*8 bytes
	brne	DCloop
	rjmp	CRLF		;Command finished, goto idle loop

;----------------------------------------------------------------
	;Sn	Acquire a serial number to key "n"
Scmd:	rcall	fetchca
	andi	rbuf,$07	;1 to 7 valid
	tst	rbuf
	breq	CmdErr		;Error, drop command
	dec	rbuf		;use 0 to 6
	mov	KeyProc,rbuf	;save
	sbr	rFlags,$01	;set flag (bit 0=1)
Scwait:	wdr
	sbrc	rFlags,0	;wait for bit cleared (bit 0)
	rjmp	Scwait		;
     ;now, the key has been read, store the SN to the eeprom location
  ldi    sbuf,$31			;send 31
  rcall  out_char
	ldi	r_BytCntr,6	;6 bytes to store
	ldi	r_Zreg,1	;SN starts at the address 1
	mov	pointer,KeyProc	;get key number
	lsl	pointer		;compute eeprom address
	lsl	pointer
	lsl	pointer
ScStor:
	cbi	PORTB,1
	sbi	PORTB,1
	ld	r_CD,Z		;get data from register
	out	EEDR,r_CD	;to eeprom data register
	out	EEAR,pointer	;address in eeprom
	rcall	WReeprom
	inc	r_Zreg		;pointer to next byte
	inc	pointer
	dec	r_BytCntr	;byte counter all bytes stored?
	brne	ScStor		;no, branch
  ;now initialize relay timers to 5 seconds by default
	ldi	r_cd,$05	;default 5 seconds
	out	EEDR,r_CD	;to eeprom data register
	out	EEAR,pointer	;Relay 1
	rcall	WReeprom
	inc	pointer
	out	EEAR,pointer	;Relay 2
	rcall	WReeprom
	rjmp	UpdEEcrc	;Go to update eeprom CRC

;----------------------------------------------------------------
	;Z	Zero all counters, SN keys, and compute eeprom CRC
Zcmd:	ldi	pointer,$00
	ldi	r_BytCntr,63
	out	EEDR,pointer	;00 to eeprom data register
ZcLoop:
	out	EEAR,pointer	;address
	rcall	WReeprom
	inc	pointer
	dec	r_BytCntr	;byte counter all bytes stored?
	brne	ZcLoop		;no, branch
	rjmp	UpdEEcrc
;----------------------------------------------------------------
	;Update eeprom CRC
UpdEEcrc:
	ldi	pointer,$00
	mov	CalcCRC,pointer	;init CRC to 0
	ldi	r_BytCntr,63
UpdLoop:
	out	EEAR,pointer	;address
	rcall	RDeeprom
	in	r_CD,EEDR	;get data
	rcall	Do_CRC		;compute CRC
	inc	pointer
	dec	r_BytCntr	;byte counter all bytes stored?
	brne	UpdLoop		;no, branch
  ;Store the calculated CRC
	out	EEDR,CalcCRC	;place CRC to the last location
	out	EEAR,pointer	;address
	rcall	WReeprom	;Store CRC
  ;Now the NVram checksum is OK, we can turn the led ON
	cbr	rFlags,CsumER	;Clear flag CsumER (bit=0)
	cbi	PORTB,$02	;PB2 set to 0 (Turn the LED: ON)
	rjmp	cmdOK		;OK, terminate
;----------------------------------------------------------------
	;Command Error send *,CR,LF
CmdErr:	ldi	sbuf,$2A	;send '*'
        rcall	out_char
	rjmp	CRLF
CmdOK:	;print OK then CR and LF
	ldi	sbuf,13		;send CR and LF
        rcall	out_char
        ldi	sbuf,10
        rcall	out_char
        ldi	sbuf,$4F	;send 'OK'
        rcall	out_char
        ldi	sbuf,$4B	;'K'
        rcall	out_char
	;Send CR and LF
CRLF:	ldi	sbuf,13		;send CR LF and signon
        rcall	out_char
        ldi	sbuf,10
        rcall	out_char
        ldi	sbuf,$45	;'E-Key>'
        rcall	out_char
        ldi	sbuf,$2D	;'-'
        rcall	out_char
        ldi	sbuf,$4B	;'K'
        rcall	out_char
        ldi	sbuf,$65	;'e'
        rcall	out_char
        ldi	sbuf,$79	;'y'
        rcall	out_char
        ldi	sbuf,$3E	;'>'
        rcall	out_char
     ;Re Init WatchDog
	ldi	r_CD,0b00001111	;Enable WD and set prescaler to 2048000cycles
	out	WDTCR,r_CD	;To Watchdog Timer control register
	rjmp	idle
;****************************************************************
;            Here from external Interrupt
; An interrupt has arrived because an Ibutton chip has been 
; applied on the E-Key interface.
;****************************************************************
ext_int0:
	in	SREGsav,SREG   ; save status
	sbis	PIND,1		;interrupt from RXD
	rjmp	in_char
        sbic	PIND,2		;interrupt from IButton
	rjmp	falseInt	;ignore pending interrups

	WLOOP	$A1,$04		;wait 480 us
Send_Command:
	ldi	r_CD,GetSN	;Command
	ldi	r_BitCntr,07	;7 bits to send
	sbi	DDRD,DDD2	;place PD2 as output
scnb:	cbi	PORTD,2
	WsLOOP	$0A		;10us (Tlow)
	ror	r_CD		;place lsb into C
	brcc	txc		;branch if tx a 0
	sbi	PORTD,2
txc:	
	WLOOP	9,10		;wait end of tslot
	sbi	PORTD,2		;end of tslot
	WsLOOP	$0A		;wait 10us (trec)
	dec	r_BitCntr
	brpl	scnb		;branch to send next bit
		;wait xxus before getting data from IB
	WsLOOP	$FF		;wait some us

GetDataBtyes:
	ldi	r_Zreg,00	;get 8 data bytes and store 
	ldi	r_BytCntr,$8	;them from R0 to R7
ReadByt:
	ldi	r_BitCntr,$8	;set bit counter
ReadBit:
	sbi	DDD2,PD2	;pd2 as output
	cbi	PORTD,PD2	;pd2 set to 0
	WsLOOP	$03		;Wait Tlowr (3us)
	cbi	DDD2,PD2	;pd2 is an input
	sbi	PORTD,PD2	;activate internal pullup
	WsLOOP	$0A		;wait for sample data
	clc			;clear carry	
	sbic	PIND,PD2	;SAMPLE DATA BIT ,and skip if PD2=0
	sec			;set carry
	ror	r_CD
	WsLOOP	$0E		;wait 15us Trelease
	WsLOOP	$4E		;wait until end of tslot 
	dec	r_BitCntr	;8 bits read ?
	brne	ReadBit		;no, get another bit
	st	Z,r_CD		;yes, store readen byte
	inc	r_Zreg		;pointer to store next byte
	dec	r_BytCntr	;byte counter a bytes readen?
	brne	ReadByt		;no, branch
  ;Now, we have to control the readen datas with the CRC
	ldi	r_Zreg,00	;get 7 data bytes ... 
	ldi	r_BytCntr,8	;... datas from R0 to R7
	eor	CalcCRC,CalcCRC	;init to 0
IB_ckCRC:
	ld	r_CD,Z		;get byte
	rcall	Do_CRC
	inc	r_Zreg		;pointer to next byte
	dec	r_BytCntr	;all bytes checked?
	brne	IB_ckCRC		;no, branch
   ;CRC has been calculated
	tst	CalcCRC
	breq	Action		;
	ldi	sbuf,$23	;send # as a CRC key error
	rcall	out_char
	rjmp	falseInt	;Invalid CRC then its a false int

Action:	;A key has been correctly readen, the CRC is OK
	;then do the proper action
;Now we have read the IB, send 7 first registers to the console
        ldi    sbuf,13		;send CRLF
        rcall  out_char
        ldi    sbuf,10
        rcall  out_char
        ldi    ZL,0		;initialize pointer
loop:   mov    hex_buf,ZL
        andi   hex_buf,15
        brne   nocr
        ldi    sbuf,13		;CRLF after 16 registers
        rcall  out_char
        ldi    sbuf,10
        rcall  out_char
nocr:   ld     hex_buf,Z        ; load register to hex_buf
        rcall  out_hex1         ; and output both nibbles
        rcall  out_hex0
        ldi    sbuf,$20
        rcall  out_char
        inc    ZL
        cpi    ZL,07		;7 registers processed ?
        brne   loop		;no, loop
        ldi    sbuf,13		;Yes! send CRLF and finish.
        rcall  out_char
        ldi    sbuf,10
        rcall  out_char
;
	wdr			;watchdog Reset
;
	sbrc	rFlags,0	;skip if command S not pending (bit 0 = 0)
	rjmp	endInt		;1, S command pending branch
	sbrc	rFlags,1	;Skip if NVram cksum error is not set (bit 1 = 0)
	rjmp	endInt		;1, Flag NVram cksum error is set, branch

   ;no, look eeprom and activate proper output	
   ;search eeprom for a corresponding serial number
	ldi	pointer,$00	;first eeprom location
NextKL:	ldi	r_Zreg,$01	;to the first byte 
	ldi	offset,$00	;init byte offset
	ldi	r_BytCntr,$06	;6 bytes to compare
	add	offset,pointer	;
NextBL:	out	EEAR,offset	;get byte from eeprom
	rcall	RDeeprom
	in	r_CD,EEDR	;get data
	ld	scratch1,Z	;data pointer to readen key bytes
	cp	scratch1,r_CD	;test
	brne	NextKey		;no match, test next key in eeprom
	inc	r_Zreg		;match, inc pointers
	inc	offset
	dec	r_BytCntr		;byte counter
	brne	NextBL		;to next Byte Location
ActionOK:	;OK, activate outputs needed
	out	EEAR,offset	;address
	rcall	RDeeprom
	in	scratch1,EEDR	;get first-output time
	inc	offset
	out	EEAR,offset	;address
	rcall	RDeeprom
	in	scratch2,EEDR	;get second-output time
SetPri:	tst	scratch1
	breq	SetSec		;branch if timing=00
	cbi	PORTB,0		;ACTIVATE OUTPUT 0
SetSec:	tst	scratch2
	breq	AcLoop		;branch if timing=00
	cbi	PORTB,1		;ACTIVATE OUTPUT 1
AcLoop:		;Action Loop, wait 1 second (approx)
	;---- WAIT 1 SECOND ----
	ldi	scnt,$10
ALl2:	wdr			;Watchdog Reset
	ldi	r_cntH,$A0
ALl0:	ldi	r_cntL,$FF
ALl1:	dec	r_cntL
	nop
	brne	ALl1
	dec	r_cntH
	brne	ALl0
	dec	scnt
	brne	ALl2
	;-----------------------
	tst	scratch1
	breq	out0		;already 00
	dec	scratch1	;decrement counter
	brne	out0		;not 00, continue loop
	sbi	PORTB,0		;DE-ACTIVATE OUTPUT 0
out0:	tst	scratch2
	breq	out1		;already 00
	dec	scratch2	;decrement counter
	brne	out1		;not 00, continue loop
	sbi	PORTB,1		;DE-ACTIVATE OUTPUT 1
out1:	tst	scratch1	;re test 0
	brne	AcLoop		;both counters are not 00
	tst	scratch2	;re test 1
	brne	AcLoop		;
	rjmp	endInt		;here when both counters reach 00

NextKey:	;go to the next key location
	ldi	scratch1,$08
	add	pointer,scratch1;
	cpi	pointer,$38
	brne	NextKL
NoValid:
endInt:
	cbr	rFlags,$01	;clear flag bit 0
falseInt:

	out	SREG,SREGsav
	reti			;end of Interrupt

;******************************************************************
;EEPROM Access Handler
;--------------------------------------------------------------------
RDeeprom:	;Read eeprom address is in EEAR, data in EEDR
	sbi	EECR,EERE	;set EEReadEnable
REwait:	sbic	EECR,EERE
	rjmp	REwait		;wait end of read eeprom
	ret

WReeprom:	;Write to eeprom
	sbi	EECR,EEWE	;set EEWriteEnable
WEwait:	sbic	EECR,EEWE
	rjmp	WEwait		;wait end of read eeprom

	ret


;******************************************************************
;Serial Handlers
;--------------------------------------------------------------------
; out_hex1 : output higher nibble from hex_buf
; hex_buf is not changed
;
out_hex1:
        mov    sbuf,hex_buf
        swap   sbuf       ; msb into lower position
        rjmp   out_hnib
;
; out_hex0 : output lower nibble from hex_buf
; hex_buf is not changed
;
out_hex0:
        mov    sbuf,hex_buf
        rjmp   out_hnib

out_hnib:
        andi   sbuf,$0F   ; mask off nibble
        subi   sbuf,-6    ; add 6 ; bit 3 is set if >9
        sbrc   sbuf,4     ; offset to add depends on case
        subi   sbuf,-7    ; for 0 we add 48=6+42
        subi   sbuf,-42   ; for A we add 55=6+7+42
        rjmp   out_char        ; and output this character

;--------------------------------------------------------------------
; output the character in sbuf
; via RS232 at 9600 bits/sec on PORTD,0

out_char:
        ldi    scnt,11		;1-START 8-DATA 2-STOP bits
bit_loop:
	cpi    scnt,11		; start-bit ?
	breq   txmark		;start bit is mark
	cpi    scnt,3		;stop bits ?
	brcs   txspace		;stop bits are space
        ror    sbuf		;transfer LSB first
        brcs   txspace		;a 1 translates to space
txmark: sbi    PORTD,0		;mark is HIGH
        rjmp   goon
txspace:cbi    PORTD,0		;spac is LOW
goon:
	ldi    r_cntL,100	;wait 100*4 cycles
bl1:	dec    r_cntL
        nop
        brne	bl1		;time-loop-end
        dec    scnt		;count down bit number
        brne   bit_loop		;loop of all bits
        ret

;--------------------------------------------------------------------
;fetchca wait for a character from serial input, and echo it 
;        result in rbuf
fetchca:wdr			;first: watchdog reset
	tst	mail		;check if character received
	breq	fetchca
	clr	mail		;YES a char has been received
	mov	sbuf,rbuf	;echo it
	rcall	out_char	;to txd
	ret

;fetchhex wait for an hexa character from input
;       no complete check done, result in hex_buf
fetchhex:
	wdr			;watchdog reset
	tst	mail		;check if character received
	breq	fetchhex
	clr	mail		;YES a char has been received
	mov	sbuf,rbuf	;echo it
	rcall	out_char	;to txd
	subi	rbuf,$30	;
	cpi	rbuf,$0A
	brmi	fhlnib
	subi	rbuf,$07	;else result between 11h and 17h
fhlnib:	;Fetch low Nibble
	andi	rbuf,$0F	;clear high nibble
	swap	rbuf		;Swap nibbles
	mov	hex_buf,rbuf	;save high nibble to hex_buf
fh1:	wdr
	tst	mail		;check if character received
	breq	fh1
	clr	mail		;YES a char has been received
	mov	sbuf,rbuf	;echo it
	rcall	out_char	;to txd
	subi	rbuf,$30	;
	cpi	rbuf,$0A
	brmi	fhok
	subi	rbuf,$07	;else result between 11h and 17h
fhok:	or	hex_buf,rbuf
	ret

;--------------------------------------------------------------------
; input a character from RS232 on portd PD1 
; Result to sbuf
; RS232 at 9600 bits/sec on PORTD,1
in_char:
	ldi	scnt,9		;start bit plus 8 data bits
RX_loop:
	ldi	r_cntL,50	;wait first half bit time
rx_t1:  dec	r_cntL		;9600 bits at 4 Mhz 1 bit = 416.66 cycles
        nop
        brne	rx_t1
        clc			;sample input line and rotate into buffer
        sbic	PIND,1		;test PD1 (RXD)
        sec
TEST:
	sbi	PORTD,0
	cbi	PORTD,0

        ror	rbuf
	ldi	r_cntL,50	;wait second half bit time
rx_t3:  dec	r_cntL
        nop
	brne	rx_t3
	dec	scnt
	brne	RX_loop
	ldi	mail,1
false1:	out	SREG,SREGsav
	WLOOP	30,30		;TEST
        reti


;******************************************************************
;CRC Handler
;--------------------------------------------------------------------
Do_CRC:	mov	Scratch2,r_CD	;save byte to compute
	ldi	r_BitCntr,8	;8 bits to compute
CRC_Loop:
	mov	r_CD,Scratch2
	eor	r_CD,CalcCRC
	ror	r_CD		;Data (lsb) into Carry
	mov	r_CD,CalcCRC
	brcc	CRCZ		;if carry =0 branch
	ldi	Scratch1,$18	;if carry=1 do eor with 18H
	eor	r_CD,Scratch1
CRCZ:	ror	r_CD
	mov	CalcCRC,r_CD
	mov	r_CD,Scratch2
	lsr	r_CD		;position to next data bit
	mov	Scratch2,r_CD	;save 
	dec	r_BitCntr
	brne	CRC_Loop
	ret

;******************************************************************

