; PlayStation Pad -> Pseudo Radio Controller Transmitter
;   ver1.1 (c) Junichi Akita
;   (this source is written for MPASM, Microchip Assemble)
;
; Connections:
;  PS Analog Padr     PIC16F84A [X'tal:19.660MHz]
;  1(DAT)----------p---PB0
;  2(CMD)--------------PB1
;  4(GND)--------------GND
;  5(+3V)--------------VDD(+5V)
;  6(SEL)--------------PB2
;  7(CLK)--------------PB3
;  9(ACK)----------p---PB4
;                      PB5------------>--Radio TX
;                 [p : pull-up to +5V via 1kohm]
;
; * for details of PS controller, see below:
;   http://www.vector.co.jp/soft/data/game/se020797.html

	list      p=16F84A
	#include <p16F84A.inc>
	__CONFIG   _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC
CDAT	equ	0	; PB0(i)
CCMD	equ	1	; PB1(o)
CSEL	equ	2	; PB2(o)
CCLK	equ	3	; PB3(o)
CACK	equ	4	; PB4(i)
TX0	equ	5	; PB5(o)

; nuber of 4byte cycles per one PWM cycle
NCYCLE	equ	7

; variables
W_TMP	equ	0x0c
S_TMP	equ	0x0d
TXD0	equ	0x0e
TXD1	equ	0x0f
TXD2	equ	0x10
TXD3	equ	0x11
CTXD	equ	0x12
CRXD	equ	0x13
LVAL	equ	0x14
RVAL	equ	0x15
ID	equ	0x16
COUNT	equ	0x17
TCOUNT	equ	0x18
TXD	equ	0x19
NTXD	equ	0x1a
CYCLE	equ	0x1b
WAIT	equ	0x1c
WAIT2	equ	0x1d
WMAP	equ	0x1e

; start up
	org	0
	goto	main
;------------------------------------------
; interrupt routine
; * this interrupt routine should be called every 1/4800=208.3us
	org	4
	movwf	W_TMP
	movf	STATUS, w
	movwf	S_TMP
; send 1 byte of TXD in order
	movlw	0
	subwf	TCOUNT, w
	btfsc	STATUS, Z
	goto	bstart	; send START bit if TCOUNT==0
;
	movlw	9
	subwf	TCOUNT, w
	btfsc	STATUS, Z
	goto	bstop	; send STOP bit if TCOUNT==9
;
	btfss	TXD, 0
	bcf	PORTB, TX0
	btfsc	TXD, 0
	bsf	PORTB, TX0
	rrf	TXD, f
	incf	TCOUNT, f
	goto	intend	
; sending start bit
bstart	bcf	PORTB, TX0
	incf	TCOUNT, f
; preparing data to send
ljudge
	movlw	0x07
	andwf	LVAL, w ; absolute of left value -> W
	subwf	CYCLE, w
	btfsc	STATUS, C ; if |LVAL| > CYCLE -> motor ON
	goto	rjudge

lid0	btfss	PORTA, 0
	goto	lid1

	btfss	LVAL, 3
	bsf	TXD0, 6 ; left motor as FWD
	btfsc	LVAL, 3
	bsf	TXD0, 5 ; left motor as BWD

lid1	btfss	PORTA, 1
	goto	lid2

	btfss	LVAL, 3
	bsf	TXD0, 1
	btfsc	LVAL, 3
	bsf	TXD0, 0

lid2	btfss	PORTA, 2
	goto	lid3

	btfss	LVAL, 3
	bsf	TXD1, 3
	btfsc	LVAL, 3
	bsf	TXD1, 2

lid3	btfss	PORTA, 3
	goto	lid4

	btfss	LVAL, 3
	bsf	TXD2, 5
	btfsc	LVAL, 3
	bsf	TXD2, 4

lid4	btfss	PORTA, 4
	goto	rjudge

	btfss	LVAL, 3
	bsf	TXD2, 0
	btfsc	LVAL, 3
	bsf	TXD3, 6

rjudge
	movlw	0x07
	andwf	RVAL, w ; absolute of right value -> W
	subwf	CYCLE, w
	btfsc	STATUS, C
	goto	restore

rid0	btfss	PORTA, 0
	goto	rid1

	btfss	RVAL, 3
	bsf	TXD0, 4 ; right motor as FWD
	btfsc	RVAL, 3
	bsf	TXD0, 3 ; right motor as BWD

rid1	btfss	PORTA, 1
	goto	rid2

	btfss	RVAL, 3
	bsf	TXD1, 6
	btfsc	RVAL, 3
	bsf	TXD1, 5

rid2	btfss	PORTA, 2
	goto	rid3

	btfss	RVAL, 3
	bsf	TXD1, 1
	btfsc	RVAL, 3
	bsf	TXD1, 0

rid3	btfss	PORTA, 3
	goto	rid4

	btfss	RVAL, 3
	bsf	TXD2, 3
	btfsc	RVAL, 3
	bsf	TXD2, 2

rid4	btfss	PORTA, 4
	goto	restore

	btfss	RVAL, 3
	bsf	TXD3, 5
	btfsc	RVAL, 3
	bsf	TXD3, 4

restore
	movlw	TXD0
	movwf	FSR
	movf	NTXD, w
	addwf	FSR, f
	movf	INDF, w
	movwf	TXD	; TXD <- TXD0 - TXD4 according to NTXD

	incf	NTXD, f
	movlw	4
	subwf	NTXD, w
	btfss	STATUS, Z
	goto	intend

	clrf	NTXD	; clear NTXD if NTXD==4
	incf	CYCLE, f ; inclement CYCLE, of 4 bytes are sent

	movlw	NCYCLE
	subwf	CYCLE, w
	btfsc	STATUS, Z
	clrf	CYCLE

	clrf	TXD0
	clrf	TXD1
	clrf	TXD2
	clrf	TXD3
	bsf	TXD0, 7	; initialize TXDn

	goto	intend
; sending stop bit
bstop	bsf	PORTB, TX0
	movlw	0
	movwf	TCOUNT
	goto	intend
; finishing interrupt routine
intend	movf	S_TMP, w
	movwf	STATUS
	swapf	W_TMP, f
	swapf	W_TMP, w
	bcf	INTCON, T0IF
	retfie
;------------------------------------------
; communicate with PS pad; send CTXD and receive CRXD
ccom	movlw	8
	movwf	COUNT
	clrf	CRXD
ccloop	bcf	PORTB, CCLK
	call	ccwait
	btfsc	CTXD, 0
	bsf	PORTB, CCMD
	btfss	CTXD, 0
	bcf	PORTB, CCMD
	rrf	CTXD, f
	rrf	CRXD, f
	btfsc	PORTB, CDAT
	bsf	CRXD, 7
	btfss	PORTB, CDAT
	bcf	CRXD, 7
	bsf	PORTB, CCLK
	call	ccwait
	decfsz	COUNT, f
	goto	ccloop

; wait for 100us (ACK time)
	movlw	0x80
	movwf	WAIT
w100lp	nop
	decfsz	WAIT, 1
	goto	w100lp

	return

; waiting half of clock cycle for PAD, 0.2us * 60 = 12us
ccwait	movlw	20
	movwf	WAIT
ccwait0	decfsz	WAIT, f
	goto	ccwait0
	return

; remap 0x00-0xff value from controller in CRXD to L/R VAL into W as follows:
; 0x00-0x0f : +7=0x07 <- 0
; 0x10-0x1f : +6=0x06 <- 1
; 0x20-0x2f : +5=0x05 <- 2
; 0x30-0x3f : +4=0x04 <- 3
; 0x40-0x4f : +3=0x03 <- 4
; 0x50-0x5f : +2=0x02 <- 5
; 0x60-0x6f : +1=0x01 <- 6
; 0x70-0x8f :  0=0x00
; 0x90-0x9f : -1=0x09 <- 9
; 0xa0-0xaf : -2=0x0a <- a
; 0xb0-0xbf : -3=0x0b <- b
; 0xc0-0xcf : -4=0x0c <- c
; 0xd0-0xdf : -5=0x0d <- d
; 0xe0-0xef : -6=0x0e <- e
; 0xf0-0xff : -7=0x0f <- f
remap	movlw	0x90
	subwf	CRXD, w	; CRXD-0x90 -> W
	btfsc	STATUS, C
	goto	rminus	; if CRXD >= 0x90
	movlw	0x70
	subwf	CRXD, w	; CRXD-0x70
	btfss	STATUS, C
	goto	rplus	; if CRXD < 0x70
	movlw	0x00	; in case of ZERO
	return
rminus	rrf	CRXD, f
	rrf	CRXD, f
	rrf	CRXD, f
	rrf	CRXD, f
	movlw	0x0f
	andwf	CRXD, w
	return
rplus	movlw	0x07
	movwf	WMAP
	rrf	CRXD, f
	rrf	CRXD, f
	rrf	CRXD, f
	rrf	CRXD, f
	movlw	0x07
	andwf	CRXD, w
	subwf	WMAP, w	; 0x07 - CRXD -> W
	return

; main routine
main
	bsf	PORTB, TX0
	bsf	PORTB, CCLK
	bsf	PORTB, CSEL
	bsf	STATUS, RP0
	movlw	0x1f
	movwf	PORTA
	movlw	0x11
	movwf	PORTB
	movlw	0x81
	movwf	OPTION_REG ; 1:4 for TMR0, 19.6608MHz / (4*256 * 4) = 4800Hz 
	bcf	STATUS, RP0
	clrf	TXD0
	clrf	TXD1
	clrf	TXD2
	clrf	TXD3
	bsf	TXD0, 7
	clrf	TCOUNT
	clrf	NTXD
	clrf	CYCLE
	clrf	TMR0
	bsf	INTCON, T0IE
	bsf	INTCON, GIE

; main loop
acq	bsf	PORTB, CCLK
	bcf	PORTB, CSEL


	movlw	0x80
	movwf	WAIT
w100lp2	nop
	decfsz	WAIT, 1
	goto	w100lp2


; 1st byte from PAD
	movlw	0x01
	movwf	CTXD
	call	ccom
; 2nd byte from PAD
	movlw	0x42
	movwf	CTXD
	call	ccom

;; check analog pad by detecting 0x3 for lower 4 bits of 2nd received data
;	movlw	0x0f
;	andwf	CRXD, f
;	movlw	0x03
;	subwf	CRXD, w
;	btfss	STATUS, Z
;	goto	neg

; 3rd byte from PAD
	movlw	0x00
	movwf	CTXD
	call	ccom
; 4th byte from PAD
	movlw	0x00
	movwf	CTXD
	call	ccom
; 5th byte from PAD
	movlw	0x00
	movwf	CTXD
	call	ccom
; 6th byte from PAD
	movlw	0x00
	movwf	CTXD
	call	ccom
; 7th byte from PAD
	movlw	0x00
	movwf	CTXD
	call	ccom
	call	remap
	movwf	LVAL
; 8th byte from PAD
	movlw	0x00
	movwf	CTXD
	call	ccom
; 9th byte from PAD
	movlw	0x00
	movwf	CTXD
	call	ccom
	call	remap
	movwf	RVAL
neg	bsf	PORTB, CSEL
; wait 16ms for next SEL cycle
	movlw	0x50	; = 80
	movwf	WAIT2
wait16a	movlw	0xc8	; = 200
	movwf	WAIT
wait16b	nop
	nop
	decfsz	WAIT, f
	goto	wait16b
	decfsz	WAIT2, f
	goto	wait16a
	goto	acq
	END

