;new program to set up and test the PN532 chip reader 
;compile with gavrasm write_pn532.asm ;flash with avrdude -c avrisp -p m168 -P /dev/tty.usbmodem1411 -b 19200 -U flash:w:write_pn532.asm
;
; I/O pins:
;	** use PORTB except RDYN on PORTD **
;	** SCK on pin 5 configured as output **
;	** MISO on pin 4 configured as input with pullup **
;	** MOSI on pin 3 configured as output **
;	** NSS on pin 2 configured as output **
;	** NSS on pin 2 not selected (high)
;	** IRQ	on PB1 configured as input with pullup **
;---- defines ----
.device atmega168
.equ HAL_PORT = 0x05		;PORTB
.equ HAL_DDR = 0x04		;DDRB
.equ HAL_PIN = 0x03		;PINB
.equ HAL_SCK = 5
.equ HAL_MISO = 4
.equ HAL_MOSI = 3
.equ HAL_NSS = 2
; not using IRQ
;
.equ MSG_FLG = 0
.equ RD_FLG = 1
;
;UBBR value for USART from f_cpu 1000000
.equ UBBRvalue = 12
;*** some opcodes for PN532 ***
.equ C_TEST = 0x00
;*** delay constant
.equ dlp_init = 50000
;---- registers
.def temp = r16
.def count = r17
.def temp2 = r18
.def r_dcs = r19
.def delayL = r24
.def delayH = r25
.def xL = r26
.def xH = r27
.def yL = r28
.def yH	= r29
.def zL = r30
.def zH = r31
.cseg
;---- Interrupt Vector ---
.org 0
	rjmp	reset
	rjmp	RDYN_L			;INT0 vector
RDYN_L:
; I'm not going to use this yet
	reti
reset:
;Initialize Stack
	ldi	temp,low(RAMEND)
	out	SPL,temp
	ldi	temp,high(RAMEND)
	out 	SPH,temp
;Init SPI
;** don't have to set phase or polarity. PN532 uses mode 0
	in	temp,SPCR
	sbr	temp,(1 << SPR0)	;divide f_cpu by 16, better for breadboards
	sbr	temp,(1 << MSTR)	;set AVR to SPI Master mode
	sbr	temp,(1 << DORD)	;use LSB bit order
	sbr	temp,(1 << SPE)		;enable SPI
	out	SPCR,temp
;
;*** set ddr and output on pin lines
;
	sbi	HAL_DDR,HAL_NSS		;output on NSS
	cbi	HAL_PORT,HAL_NSS	;lower NSS to wake up pn532
	sbi	HAL_DDR,HAL_SCK		;output on SCK
	sbi	HAL_PORT,HAL_MISO	;set pullup on MISO // confg as input
	sbi	HAL_DDR,HAL_MOSI	;output on MOSI
; wait to set pullup on IRQ line sbi	HAL_PORT,HAL_IRQ	;set pullup on IRQ line
;
;initialize USART
        ldi     temp,high(UBBRvalue)    ;baud rate param
        sts     UBRR0H,temp
        ldi     temp,low(UBBRvalue)
        sts     UBRR0L,temp
        lds     temp,UCSR0A
        ori     temp,(1 << U2X0)        ;set use 2x because %error actual baud > .5
        sts     UCSR0A,temp
;--- USART register values
        ldi     temp,(1 << TXEN0) | (1 << RXEN0) ;enable transmit and receive
        sts     UCSR0B,temp
        ldi     temp,(1 << UCSZ01) | (1 << UCSZ00) ;8 data bits, 1 stop bit
	sts	UCSR0C,temp
;********* Main Program **********
;
; wake up pn532
	ldi	count,5			;one second
	rcall	delay			;delay after startup
start:	
	ldi	zH,high(gfv << 1)	;call get firmware version to sync?
	ldi	zL,low(gfv << 1)
	rcall	snd_cmd
	sbi	HAL_PORT,HAL_NSS	;snd_cmd doesn't raise NSS when finished
	rcall	ack_resp
	ldi	zH,high(samc << 1)	;next configure SAM normal 14 01
	ldi	zL,low(samc << 1)
	rcall	snd_cmd
	sbi	HAL_PORT,HAL_NSS
	rcall	ack_resp
	ldi	zH,high(ilpt << 1)
	ldi	zL,low(ilpt << 1)
	rcall	snd_cmd			;send inListPassiveTarget command
	sbi	HAL_PORT,HAL_NSS
	rcall	ack_resp		;get response
	ldi	xL,low(in_buf)
	ldi	xH,high(in_buf)
	adiw	xH:xL,9
	ld	temp,X+
	cpi	temp,4			;make sure NFCID is MiFare1
	brne	error
	ldi	zH,high(ide_a << 1)
	ldi	zL,low(ide_a << 1)
 	rcall	snd_cmd			;send first part of command
	ldi	count,4
	ldi	r_dcs,0x76		;dcs before NFCID
auth_end:
	ld	temp,X+
	add	r_dcs,temp
	rcall	SPI_tradeByte
	dec	count
	brne	auth_end	
	mov 	byte_tx,r_dcs
	rcall	transmit	
	neg	r_dcs
	mov 	byte_tx,r_dcs
	rcall	transmit
	mov 	temp,r_dcs
	rcall	SPI_tradeByte
	ldi	temp,0x00
	rcall	SPI_tradeByte
	sbi	HAL_PORT,HAL_NSS
	rcall	ack_resp
	ldi	xL,low(in_buf)
	ldi	xH,high(in_buf)
	adiw	xH:xL,4			;check response status
	ld	temp,X
	cpi	temp,0x00
	brne	error
	ldi	zH,high(cmd_head << 1)
	ldi	zL,low(cmd_head << 1)
	rcall	snd_cmd
	ldi	zH,high(wr_name << 1)		;write data block 'Darby Crash'
	ldi	zL,low(wr_name << 1)
	rcall	snd_dcs
	sbi	HAL_PORT,HAL_NSS
	rcall	ack_resp
	ldi	zH,high(cmd_head << 1)
	ldi	zL,low(cmd_head << 1)
	rcall	snd_cmd
	ldi	zH,high(credit_blk << 1)	;write value block 667
	ldi	zL,low(credit_blk << 1)
	rcall	snd_dcs
	sbi	HAL_PORT,HAL_NSS
	rcall	ack_resp
	ldi	zH,high(cmd_head << 1)
	ldi	zL,low(cmd_head << 1)
	rcall	snd_cmd
	ldi	zH,high(read_st7 << 1)		;read sector trailer access bits
	ldi	zL,low(read_st7 << 1)
	rcall	snd_dcs
	sbi	HAL_PORT,HAL_NSS
	rcall	ack_resp
;done
	ldi	zH,high(succ_str << 1)
	ldi	zL,low(succ_str << 1)
	rcall	print_s			;print success
	rjmp	exit_lp	
error: 	ldi 	byte_tx,0xEE
	rcall	transmit
	rcall	transmit		;EE EE on error
exit_lp:
	nop
	rjmp exit_lp
;****** Subroutines ******
;print out message
prt_m:
	push	count
	ldi	xL,low(in_buf)
	ldi	xH,high(in_buf)		;load x reg with pointer to message
	ld	count,X+
	mov	byte_tx,count
	rcall	transmit
p_lp:	ld	byte_tx,X+
	rcall	transmit
	dec	count
	brne	p_lp
	pop	count
	ret
.def byte_tx = r19
;---- function to transmit byte ----
;transmit byte from r19 over usart
transmit:
        lds     temp,UCSR0A
        sbrs    temp,UDRE0
        rjmp    transmit
        sts     UDR0,byte_tx
        ret
;print string function
;takes Z loaded with pointer to string with first byte being length
;uses r16,r17,r19
print_s:
        lpm     count,Z+
for1:   lpm     byte_tx,Z+
wait:   lds     temp,UCSR0A
        sbrs    temp,UDRE0      ;wait for Tx buffer to be empty
        rjmp    wait            ;not ready
        sts     UDR0,byte_tx;
        dec     count
        brne    for1
        ret
;
;-------- delay subroutines ------------
;takes count in reg r17 * 200 milliseconds
delay:
	ldi     delayH,high(dlp_init)   
        ldi     delayL,low(dlp_init)
dlp:    sbiw    delayH:delayL,1
	nop				;makes 4 clock cycles
        brne    dlp 
        dec     count
        brne    delay
        ret 
;millisecond delay *** takes count (r17) as msecond value
m_delay:
	ldi	temp,0xfa
md_lp:	nop
	nop
	dec	temp
	brne	md_lp			;4 clock cycles times 250 = 1000us (1000000 f_cpu)
	dec	count
	brne	m_delay
	ret
;----- SPI routines -----
;uses temp r16 as tx byte and returns with rx byte in temp
SPI_tradeByte:
	out	SPDR,temp
lp1:	in	temp2,SPSR
	sbrs	temp2,SPIF
	rjmp 	lp1
	in	temp,SPDR
	ret
;send ascii representation of one byte over serial
;byte to convert in r24
;uses r24 as low nibble to send and r25 for high nibble
t_htoa:
        lds     temp,UCSR0A
        sbrs	temp,UDRE0
        rjmp	t_htoa
	ldi	temp,0x30	;ascii offset
	mov	r25,r24
	lsr	r25
	lsr	r25
	lsr	r25
	lsr	r25
	cpi	r25,0x0A
	brlt	no_e
	ldi	temp,0x37	;extended hex
no_e:	add	r25,temp
        sts     UDR0,r25
t_2:	lds	temp,UCSR0A
	sbrs	temp,UDRE0
	rjmp	t_2
	ldi	temp,0x30	;ascii offset
	andi	r24,0x0f	;low nibble
	cpi	r24,0x0a
	brlt	no_e2
	ldi	temp,0x37
no_e2:	add	r24,temp	;add 0x30 to get ascii representation
	sts	UDR0,r24
	ret
;print message from inbuf
p_mess:
	ldi	xL,low(in_buf)
	ldi	xH,high(in_buf)
	ld	count,X+
mess_lp:
	ld	byte_tx,X+		;reprint data
	rcall 	transmit
	dec 	count
	brne	mess_lp
	ret
;send command routine
;Z reg points to command string with first byte being length
snd_cmd:
	cbi	HAL_PORT,HAL_NSS
	lpm	count,Z+
snd_for:
	lpm	temp,Z+
	rcall	SPI_tradeByte
	dec	count
	brne	snd_for
	ret
; send routine with data checksum
snd_dcs:
	lpm	count,Z+
	mov	temp,count
	rcall	SPI_tradeByte
	mov	temp,count		;count is same as length byte
	neg	temp
	rcall	SPI_tradeByte		;send length byte and length checksum
	lpm	temp,Z+
	mov	r_dcs,temp		;first byte to add to (TFI)
	rcall	SPI_tradeByte
	dec	count
sdcs_for:
	lpm	temp,Z+
	add	r_dcs,temp
	rcall	SPI_tradeByte
	dec	count
	brne	sdcs_for
	mov	temp,r_dcs
	neg	temp
	rcall	SPI_tradeByte
	ldi	temp,0x00
	rcall	SPI_tradeByte
	ret
;ack routine
;
ack_resp:
	ldi	xL,low(in_buf)		;set up pointer to storage buffer
	ldi	xH,high(in_buf)
	cbi	HAL_PORT,HAL_NSS
sr_lp: 	ldi	temp,0x02		;status reading SR
	rcall 	SPI_tradeByte
	ldi	temp,0x00
	rcall 	SPI_tradeByte
	mov 	byte_tx,temp
;	rcall	transmit
	sbrs	byte_tx,0		;ready bit
	rjmp	sr_lp
	sbi	HAL_PORT,HAL_NSS
;get ack and print for now
	ldi	count,0x06		;6 bytes
	cbi	HAL_PORT,HAL_NSS
	ldi	temp,0x03		;data reading
	rcall 	SPI_tradeByte
ack_p:	ldi	temp,0x00
	rcall	SPI_tradeByte
	mov 	byte_tx,temp
	rcall	transmit
	dec 	count
	brne	ack_p
	sbi	HAL_PORT,HAL_NSS
	ldi	byte_tx,0xAC
	rcall	transmit
 	cbi	HAL_PORT,HAL_NSS
rec:	ldi	temp,0x02		;status reading
	rcall 	SPI_tradeByte
	ldi	temp,0x00
	rcall	SPI_tradeByte
	sbrs	temp,0
	rjmp	rec
	sbi	HAL_PORT,HAL_NSS
	ldi	count,0x03		;first three bytes preamble +  start code
	cbi	HAL_PORT,HAL_NSS
	ldi	temp,0x03
	rcall 	SPI_tradeByte		;data reading
out_p:	ldi	temp,0x00
	rcall	SPI_tradeByte
	mov	byte_tx,temp
	rcall	transmit		;print 
	dec	count
	brne	out_p
	ldi	temp,0x00
	rcall	SPI_tradeByte
	mov	byte_tx,temp
	rcall	transmit
	cpi	byte_tx,0x01		;length byte 1 on error
	breq	app_err
	mov	count,byte_tx
	inc	count
	inc	count
	inc	count
	st	X+,count		;store length first
rec_end:
	ldi	temp,0x00
	rcall	SPI_tradeByte
	st	X+,temp			;store rec mess
	mov	byte_tx,temp
	rcall	transmit
	dec 	count
	brne	rec_end
	sbi	HAL_PORT,HAL_NSS
	ret
app_err:
	ldi	count,0x04
err_lp:	ldi	temp,0x00
	rcall	SPI_tradeByte
	mov	byte_tx,temp
	rcall	transmit
	dec	count
	brne	err_lp	
	sbi	HAL_PORT,HAL_NSS
end:	nop				;dont return
	rjmp	end
;****** Subroutines ******
succ_str: .db 20,0x0d,0x0a,"*** Success! ***",0x0d,0x0a,0x00
cre_str: .db 24,"command response event: ",0x00
mode_str: .db 5,"mode:"
buff_str: .db 8,"buffers:",0x00
hello: .db "hello world!"
brd_str: .db 22,"*** we are live! ***",0x0d,0x0a,0x00
setup_str: .db 32,"Setup mode! starting set up...",0x0d,0x0a,0x00
;********************************************************************
;some commands to send
gfv: .db 0x0A,0x01,0x00,0x00,0xFF,0x02,0xFE,0xD4,0x02,0x2A,0x00,0x00
samc: .db 0x0D,0x01,0x00,0x00,0xFF,0x05,0xFB,0xD4,0x14,0x01,0x00,0x01,0x16,0x00
ilpt: .db 0x0C,0x01,0x00,0x00,0xFF,0x04,0xFC,0xD4,0x4A,0x01,0x00,0xE1,0x00,0x00
ide_a: .db 0x11,0x01,0x00,0x00,0xFF,0x0F,0xF1,0xD4,0x40,0x01,0x60,0x07,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
cmd_head: .db 0x04,0x01,0x00,0x00,0xFF,0x00
wr_name: .db 0x15,0xD4,0x40,0x01,0xA0,0x04,0x44,0x61,0x72,0x62,0x79,0x20,0x43,0x72,0x61,0x73,0x68,0x00,0x00,0x00,0x00,0x00
credit_blk: .db 0x15,0xD4,0x40,0x01,0xA0,0x05,0x9B,0x02,0x00,0x00,0x64,0xFD,0xFF,0xFF,0x9B,0x02,0x00,0x00,0x05,0xFA,0x05,0xFA
read_st7: .db 0x05,0xD4,0x40,0x01,0x30,0x07
.dseg
;**** in_buf starts with a length byte
in_buf: .byte 0x40