; ************************************
; * Parallelport Communication  by PEK '2000
; *
; * The program wait for a specific
; * command on the parallel-port data-
; * lines 0-3 and then it send data out on
; * the status-lines.
; *
; * The program show how to reach SRAM directly
; * and with pointers. The program also use
; * external Interrupt and Macros.
; *
; * Strobe - PD2 (Computer to activate transfer)
; * Data - PC0-3 (Data to AVR)
; * Status - PC4-7 (Data from AVR)
; * ACK - PD0 (AVR to send acknowledge)
; *
; * To Send Command to AVR :
; * - Set Data to send on data-lines (pin 0-3)
; * - Set Strobe low (to high in register)
; * - Wait for ACK to get low (to high in register)
; * - Set Strobe high (to low in register)
; * - Wait for ACK to get high (to low in register) before send a new command
; * 
; * To Get Data from AVR :
; * - Wait for ACK to get low (to high in register)
; * - Get Data from status-lines
; * - Set Strobe low (to high in register)
; * - Wait for ACK to get high (to low in register)
; * - Set Strobe high (to low in register)
; *
; * Use a 4 MHz crystal!!!
; ************************************


.include "8515def.inc"


; ************************************
; * Register and Variable Definitions             
; ************************************
.cseg
.def	temp	=R16
.def	delay1	=R17
.def	delay2	=R18
.def	delay3	=R19
.def	command	=R20			; Command from computer

.equ	PORTCIN	=PIND			; Control Input Port
.equ	PORTSOUT=PORTD			; Status Output Port
.equ	PORTIN	=PINC			; Data Input Port
.equ	PORTOUT	=PORTC			; Data Output Port
.equ	P_ACK	=PD0			; Acknowledge Pin
.equ	P_STR	=PD2			; Strobe Pin
.equ	C_SEND	=0x01			; Command to Send Data


; ************************************
; * SRAM Allocations
; ************************************
.dseg
data:	.BYTE 	2			; 2 bytes to send


; ************************************
; * Macros
; ************************************
.macro	SDTIME				; To Set Delay Times
	ldi	delay3,@0
	ldi	delay2,@1
	ldi	delay1,@2
.endmacro


; ************************************
; * Start of Code                    
; ************************************
.cseg
.org 0
	rjmp	Start
	rjmp	Strobe_Int		; External ISR
	reti
	reti


Start:	ldi	temp,low(RAMEND)
	out	SPL,temp		; Set stack pointer to last internal RAM location
	ldi	temp,high(RAMEND)
	out	SPH,temp


; ** Setup Ports and Interrupt *******
	ldi	temp,0xFB
	out	DDRD,temp		; Port D, Pin 2 (P_STR) as input and
	ldi	temp,0x04		; enable Pull-Up
	out	PORTD,temp	

	sbi	PORTSOUT,P_ACK		; Set ACK-bit (to zero)
	
	cli				; Disable global interrupts
	ldi	temp,0x40		; Enable INT0 (P_STR-pin)
	out	GIMSK, temp

	ldi	temp,0xF0
	out	DDRC,temp		; Port C, Pin 0-3 as input and
	ldi	temp,0x0F		; enable Pull-Up
	out	PORTC,temp


; ** Functions before Loop ***********
	ldi	temp,0x12		; Store values in SRAM to send
	sts	data,temp
	ldi	temp,0x5C
	sts	data+1,temp

	sei				; Enable global interrupts


; ** Main loop ***********************	
Loop:


	rjmp	Loop


; ** External ISR (Strobe) ***********
Strobe_Int:
	push	delay1			; Store registers that change
	push	delay2			; inside ISR.
	push	delay3
	push	temp
	push	ZH
	push	ZL
	push	command


	in	command,PORTIN		; Get Command from computer and
	andi	command,0x0F		; mask the 4 lower bits.

	rcall	Send_Ack		; Send Acknowledge

	cpi	command,C_SEND		; Is it the Command to Send Data ?
	breq	Send_Data


Exit_Strobe_Int:
	pop	command
	pop	ZL
	pop	ZH
	pop	temp
	pop	delay3			; Get back the original values
	pop	delay2			; of these registers.
	pop	delay1
	reti


; ** Send Data ***********************
Send_Data:
	ldi	ZH,high(data)		; Z points at data to send
	ldi	ZL,low(data)

	rcall	Send_Byte		; Send 2 bytes
	adiw	ZL,1
	rcall	Send_Byte

	rjmp	Exit_Strobe_Int


; ** Send Byte (Z) *******************
Send_Byte:
	ld	temp,Z			; Send 4 Lower Bits
	ori	temp,0xF0
	swap	temp
	out	PORTOUT,temp
	rcall	Wait_Strobe

	ld	temp,Z			; Send 4 Higher Bits
	ori	temp,0x0F
	out	PORTOUT,temp
	rcall	Wait_Strobe
	ret


; ** Send Acknowledge ****************
Send_Ack:
	cbi	PORTSOUT,P_ACK		; Clear ACK-bit (to one)
	rcall	Wait_Strobe_High
	sbi	PORTSOUT,P_ACK		; Set ACK-bit (to zero)
	ret


; ** Wait Strobe *********************
Wait_Strobe:
	cbi	PORTSOUT,P_ACK		; Clear ACK-bit (to one)
	rcall	Wait_Strobe_Low
	sbi	PORTSOUT,P_ACK		; Set ACK-bit (to zero)
	rcall	Wait_Strobe_High	
	ret


; ** Wait Strobe Routines ************
Wait_Strobe_Low:
	SDTIME	153,85,59		; Set Delay Time to 10 sek (at 4 MHz)
WSL_Loop:
	sbis	PORTCIN,P_STR		; Is Strobe low ?
	ret

	dec	delay1			; Wait MAX 10 sek for computer to
	brne	WSL_Loop		; make Strobe Low.
	dec	delay2
	brne	WSL_Loop
	dec	delay3
	brne	WSL_Loop
	
	rcall	Error
	ret	


Wait_Strobe_High:
	SDTIME	153,85,59		; Set Delay Time to 10 sek (at 4 MHz)
WSH_Loop:
	sbic	PORTCIN,P_STR		; Is Strobe High ?
	ret

	dec	delay1			; Wait MAX 10 sek for computer to
	brne	WSH_Loop		; make Strobe High.
	dec	delay2
	brne	WSH_Loop
	dec	delay3
	brne	WSH_Loop
	
	rcall	Error
	ret


; ** Error functions *****************
Error:
	ret
