;************************************************
;* PROGRAM 22-M2: PSX INTERPRETER
;*
;* Chip Number 		: U1
;* File Name 		: Program 022 PSX Interpreter.asm
;* Date				: 05.08.2004
;* Last Update		: 05.09.2004
;* Version			: 2.0.0
;* Support Telephone: no listing
;* Support Fax		: no listing
;* Support E-mail	: brm016000@utdallas.edu
;* Target MCU		: AT90S2313-10PC
;*
;* REVISED
;*
;*************************************************	
;.device AT90S2313

.nolist
.include "2313def.inc"
.list

;- Register Aliases -----
;.def					= R16		; General-purpose register by tradition
.def	loop			= R17		; Loop/increment counter by tradition
.def	addr			= R18		; Address-holder by tradition
.def	addr2			= R19		; Secondary address-holder
.def	icount			= R20		; Interrupt counter (use only within interrupt)
.def	accum2			= R21		; Interrupt-only general purpose register
.def	istatus			= R22		; Status register saver
.def 	accum3			= R23		; Interrupt-only general purpose register
;- Define Constants -----
.equ	psx_capture		= $60		; RAM location for key info
.equ	psx_status		= psx_capture+1	; Latest transfer status
.equ	psx_line1		= psx_capture+2	; 1st key group (menu/direction buttons)
.equ	psx_line2		= psx_capture+3	; 2nd key group (action buttons)
.equ	psx_line3		= psx_capture+4	; Analog button/joystick data
.equ	psx_line4		= psx_capture+5	; Analog button/joystick data
.equ	psx_line5		= psx_capture+6	; Analog button/joystick data
.equ	psx_line6		= psx_capture+7	; Analog button/joystick data

.equ	psx_delay		= 200		; Approx delay between bits
.equ	ACK_timeout		= 60 		; Time for controller to ACK

.equ	digital_con		= 0x5A		; Controller Designator
.equ	neg_con			= 0x23		; Controller Designator
.equ	analog_g_con	= 0x53		; Controller Designator
.equ	analog_r_con	= 0x73		; Controller Designator

;.equ	byte_size		= 8			; Bits to transfer
;.equ	digital_size	= 5			; Bytes to transfer
;.equ	analog_size		= 9			; Bytes to transfer

.equ	PORT_PSX		= PORTD		; Port to use for PSX ios
.equ	DDR_PSX			= DDRD		; Directions (must apply to PORT_PSX's port)
.equ	PIN_PSX			= PIND 		; Inputs (must apply to same port as PORT_PSX)

.equ	io_mask			= 0b10001101; Port directions
.equ	io_normals		= 0b00001101; Normal (idle) states for ios'

.equ	CLK				= 0			; AVR output
.equ	CLK_msk			= 0b00000001; Bit mask for output
.equ	DATA			= 1			; AVR input
.equ	DATA_msk		= 0b00000010; Bit mask for input
.equ	COMM			= 2			; AVR output
.equ	COMM_msk		= 0b00000100; Bit mask for output
.equ	ATT				= 3			; AVR output
.equ	ATT_msk			= 0b00001000; Bit mask for output
.equ	ACK				= 4			; AVR input
.equ	ACK_msk			= 0b00010000; Bit mask for input
;- Macros ---------------
.macro CLK_SHIFT
	cbi PORT_PSX,CLK	; Lower clock.  "Shift bit"
.endmacro

.macro CLK_SAMPLE
	sbi PORT_PSX,CLK	; Raise clock.  "Sample bit"
.endmacro
;------------------------

;########################
;# Flash Memory			#
;########################
.cseg					; Flash Memory: Code Segment
.org 0x00				; Align to reset vector

	rjmp main			; Reset Vector
;########################
;# Interrupt Vectors	#
;########################
	RETI		;Int0 Interrupt
	RETI		;Int1 Interrupt
	RETI		;TC1-Capture Event
	rjmp tcr1i	;TC1-Compare Match
	RETI		;TC1-Overflow
	RETI		;TC0-Overflow
	RETI		;UART Receive Transfer Complete
	RETI		;UART Data Register Empty
	RETI		;UART Transmit complete
	RETI		;Analog Comparator
;########################
;# End Interrupt Vectors#
;########################
main:
	ldi R16,io_mask			; ::: PORT DIRECTION SETUP
	out DDR_PSX,R16			; Load Input/Output configurations

	ser R16
	out DDRB,R16

	ldi R16,io_normals		; ::: PORT SETUP SETUP
	out PORT_PSX,R16		; Enable internal pull-up resistors if wanted

	ldi R16,(RAMEND)		; ::: STACK INITIALIZATION
	out SPL,R16				; Send new stack pointer

	ldi R26,PIN_PSX + $20	; ::: X-POINTER SETUP
	ldi R27,0x00			; Add "$20" to point to the register mirror in SRAM

	ldi R16,0b00001011		; ::: TIMER 1 SETUP (step B)
	out TCCR1B,R16			; Send configuration:
							; - enable "clear on compare-match"
							; - prescale: CLK / 64 = 16us period

	ldi R16,0x08			; ::: TIMER 1 COMPARE REGISTER SETUP
	out OCR1AH,R16			; Send to HIGH byte
	ldi R16,0x12 - 0x01		; 0x0411 = A 60Hz Sample Rate if with 4.00MHz clock
	out OCR1AL,R16			; Send to LOW byte

	ldi R16,0b01000000		; ::: TIMER INTERRUPTS MASK
	out TIMSK,R16			; Send configuration:
							; - enable compare-match Interrupt Service Routine

	SEI						; Global interrupt enable

;----------------------------
; * CODE DEMO
;
; The following illistrates 
; how the X-22 interrupt may
; be used in a simple 
; application
;
; This code will monitor the
; menu/direction keys and
; mirror their activity to 
; PORTD.
; * Begin DEMO code
polling_loop:
	lds R16,psx_line2		; Check menu/direction buttons
	out PORTB,R16
	rjmp polling_loop
; * End DEMO code
;----------------------------



tcr1i:		; ::: X- 22 :::::Controller Read
	rcall exx				; Save CPU status register
	cbi PORT_PSX,ATT			; Lower the "ATTENTION" line
;---FRAME 1------------------
	ldi accum2,0x01			; 0x01 is the STARTUP command
	mov R8,accum2			; Copy register
	rcall psx_get_send		; :::SEND: Wakeup 0x01 GET: nothing
							; No need to store, keypad is idle
;---FRAME 2------------------
	ldi accum2,0x42			; 0x42 is the "Request for Data" command
	mov R8,accum2			; Copy register
	rcall psx_get_send		; SEND: Request 0x42 GET: controller type
	sts psx_capture,accum3	; STORE: keypad's type number
;---FRAME 3------------------
							; No need to send commands
	rcall command_idle		; Stay idle
	rcall psx_get_send		; SEND: nothing  GET: 0x5A
							; Keypad will send "0x5A"
;---FRAME 4------------------
							; No need to send commands
	rcall command_idle		; Stay idle
	rcall psx_get_send		; SEND: nothing  GET: keys
	sts psx_capture+2,accum3; Store 1st line of key info
;---FRAME 5------------------
							; No need to send commands
	rcall command_idle		; Stay idle
	rcall psx_get_send		; SEND: nothing	 GET: keys
	sts psx_capture+3,accum3; Store 2nd line of key info
;----------------------------
	lds accum2,psx_capture	; Load captured data (controller type)
	cpi accum2,digital_con	; "Is this the digital controller?"
	brne Analogs_only		; Contine if this is an "Advanced" controller
	sbi PORT_PSX,ATT			; Raise ATTention line again
	rjmp exxe				; If digital, return to main program
;	reti					; No RETI needed
Analogs_only:
;---FRAME 6------------------
							; No need to send commands
	rcall command_idle		; Stay idle
	rcall psx_get_send		; SEND: nothing  GET: keys
	sts psx_capture+4,accum3; Store 3rd line of key/analog info
;---FRAME 7------------------
							; No need to send commands
	rcall command_idle		; Stay idle
	rcall psx_get_send		; SEND: nothing  GET: keys
	sts psx_capture+5,accum3; Store 4th line of key/analog info
;---FRAME 8------------------
							; No need to send commands
	rcall command_idle		; Stay idle
	rcall psx_get_send		; SEND: nothing  GET: keys
	sts psx_capture+6,accum3; Store 5th line of key/analog info
;---FRAME 9------------------
							; No need to send commands
	rcall command_idle		; Stay idle
	rcall psx_get_send		; SEND: nothing  GET: keys
	sts psx_capture+7,accum3; Store 6th line of key/analog info
;----------------------------
	sbi PORT_PSX,ATT			; Raise ATTention line again
	rjmp exxe				; Restore status register and exit
; reti						; No RETI necessary



command_idle:				; Send the "idle" state
	ser accum2
	mov R8,accum2
	ret

;---------------------------\
; PSX GET-SEND				;
psx_get_send:				;
;							;
; Transfer 1 byte to PSX and;
; 1 byte from PSX 			;
; simultaneously.			;
;							;
; Wait for ACKNOWLEDGE at	;
; end of transfer			;
;							;
; LSB must arrive first		;
;							;
; Lines are active-high		;
; Normally high				;
;							;
; IN: R8 = outgoing byte	;
; OUT: accum3 =received byte;
; DESTROYED:				;
;	icount = R20			;
;	accum2 = R21			;
;---------------------------/
	clr accum3				; Clear the "received byte" register
	ldi icount,8			; Number of bits to exchange?
transfer_bit:				; :: BEGIN!
next_bit:
	push icount				; Save bit tracker
	rcall delay				; Pause for bit settling
	CLK_SHIFT				; "Shift in next bit"
	ror R8					; Rotate right through carry
	brcs send_1				; Need to send a ONE?
send_0:
	cbi PORT_PSX,COMM			; Lower command line
	rjmp send_escape
send_1:
	sbi PORT_PSX,COMM			; Raise command line
	nop						; Pause for consistancy
send_escape:
	rcall delay				; Pause. Let lines settle.
	CLK_SAMPLE				; "Sample bits now!"
	ld accum2,X				; Get pins status
	sbrc accum2,DATA		; Check incoming DATA bit
	rjmp got_1
got_0:
	lsr accum3				; Shift register.  Insert ZERO in MSB.
	nop						; Pause for consistancy
	rjmp got_escape
got_1:
	sec						; Set carry flag.  Prepare to rotate
	ror accum3				; Rotate a ONE into the current MSB
got_escape:
	pop icount				; Restore bit tracker
	dec icount				; Decrement bit counter
	brne next_bit

bits_done:					; :: PAUSE!  WAIT for ACK signal
	ldi icount,ACK_timeout 	; Length of timeout period
wait_for_ack:
	dec icount				; Decrement timeout counter
	breq transfer_fail		; Fail the Xfer if no ACK arrived
	ld accum2,X				; Read PINB status
	andi accum2,ACK_msk		; Mask off unneeded bits
	brne wait_for_ack		; Loop until ACK goes low
	ret
transfer_fail:
	ret


; SHORT DELAY for bit propagation and settling
delay:	
; Total Delay time is 
; 1.00us + 1us(psx_delay)
; @ 4.00MHz
	push icount				; Save bit counter
	ldi icount,psx_delay - 1
delay_loop:
	nop						; IN: icount = length of delay
	dec icount				; Decrement loop count
	brne delay_loop			; Loop until ready
	pop icount				; Restore bit counter
	ret


exx:						; ::: EXCHANGE STATUS
	in istatus,SREG			; Read CPU Status flags
	ret
exxe:						; ::: EXCHANGE AND EXIT
	out SREG,istatus		; Restore CPU Status flags
	reti					; Return from interrupts