; ***************************************************************************
; OpenZengRealPlayers(OZRP) controller firmware for PIC16F873/876 (ver1.4)
;
;                                      (c) Junichi Akita (akita@fun.ac.jp)
;
; -Input Device : 12 PlayStation PADs with 3 MultiTaps
; -Output Data  : serial data of chaning PADs (115.2kbps)
; 
; CIRCUIT DIAGRAM
;          PIC           PS(1) PS(2) PS(3)    MAX232        DSUB9(female)
;          2(PA0)----------6-----6-----6       1- 3:0.1uF 
;          3(PA1)----------7-----7-----7       4- 5:0.1uF
;          4(PA2)----------2-----2-----2      16- 2:0.1uF 
;         21(PB0)----------1                  15- 6:0.1uF
;         22(PB1)----------------1            16-15:0.1uF
;         23(PB2)----------------------1
;         24(PB3)----------9
;         25(PB4)----------------9
;         26(PB5)----------------------9        14------------2
;         17(PC6)-------------------------------11
;         9-10:20MHzOSC
; +5V-----1&20-------------5-----5-----5--------16
; GND-----8&19-------------4-----4-----4--------15------------5
;   note: PIC    = PIC16F873/876
;         PS(1)  = PlayStationControllerConnector#1 (for PAD0-PAD3)
;         PS(2)  = PlayStationControllerConnector#2 (for PAD4-PAD7)
;         PS(3)  = PlayStationControllerConnector#3 (for PAD8-PAD11)
;         MAX232 = RS232C-TTL level converter
;         DSUB9  = serial port for connection with PC
;
; * see http://lillith.sk.tsukuba.ac.jp/~kashima/games/ps_jpn.txt
;   for details of PlayStationController connector pin assign and protocols
; * see http://www.microchip.co.jp/30292a-j.pdf for details of PIC16F873/876
; * see http://pdfserv.maxim-ic.com/arpdf/1798J.pdf for details of MAX232
;
; PAD number assignment
;  PS(1)-----MultiTap--+--A : PAD0
;                      +--B : PAD1
;                      +--C : PAD2
;                      +--D : PAD3
;  PS(2)-----MultiTap--+--A : PAD4
;                      +--B : PAD5
;                      +--C : PAD6
;                      +--D : PAD7
;  PS(3)-----MultiTap--+--A : PAD8
;                      +--B : PAD9
;                      +--C : PAD10
;                      +--D : PAD11
;
; OutputData format (RS232C@115.2kbps)
;  7 bytes for one change of each PAD
;    1st : upper 4bits=PAD type, lower 4bits=PAD number(0-11)
;    2nd : raw PAD data #1 (example: 7bit=left buttom pressed for normal pad)
;    3rd : raw PAD data #2 (example: 0bit=L2 button for normal pad)
;    4th : raw PAD data #3 (example: right handle horizontal for analog pad)
;    5th : raw PAD data #4
;    6th : raw PAD data #5
;    7th : raw PAD data #6
; * see http://lillith.sk.tsukuba.ac.jp/~kashima/games/ps_jpn.txt
;   for details of raw PAD data
;
; NOTIFICATION FOR ASSEMBLING AND WRITING:
;  - compiler : pa.exe (http://mypaso.com/pic/pa/)
;  - writer   : PIC-PGMv2, etc. (RB3 should be configured as GPIO, not LVPGM)
; ***************************************************************************

	include	16f877.h
	.osc	HS
	.wdt	off
	.pwrt	on
	.bod	off
	.protect	off
PSEL	equ	porta.0	; PA0 : CSEL  [2]
PCLK	equ	porta.1	; PA1 : CCLK  [3]
PCMD	equ	porta.2	; PA2 : CCMD  [4]
PDAT1	equ	portb.0	; PB0 : CDAT1 [21]
PDAT2	equ	portb.1	; PB1 : CDAT2 [22]
PDAT3	equ	portb.2	; PB2 : CDAT3 [23]
PACK1	equ	portb.3	; PB3 : CACK1 [24]
PACK2	equ	portb.4	; PB4 : CACK2 [25]
PACK3	equ	portb.5	; PB5 : CACK3 [26]

	org	20h	; (in bank 0)
CDAT	ds	3	; DAT from PAD[i]
CCMD	ds	1	; CMD for PADs
COUNT	ds	1
FSR0	ds	1
FSR1	ds	1
CCOUNT	ds	1
TMP	ds	1
NSTAT	ds	1
PADID	ds	12	; PAD ID
PADA	ds	12	; current PAD status A
PADB	ds	12	; current PAD status B
PADC	ds	12	; current PAD status C
PADD	ds	12	; current PAD status D
PADE	ds	12	; current PAD status E
PADF	ds	12	; current PAD status F
W_TMP	ds	1	; *context saving, should be in 0x70-0x7f
S_TMP	ds	1	; *context saving, should be in 0x70-0x7f

	org	0a0h	; (in bank 1)
PADA0	ds	12	; previous PAD status A
PADB0	ds	12	; previous PAD status B
PADC0	ds	12	; previous PAD status C
PADD0	ds	12	; previous PAD status D
PADE0	ds	12	; previous PAD status E
PADF0	ds	12	; previous PAD status F
SFLAG	ds	1
SCOUNT	ds	1
; note: end of bank 1 should be lower than 0xf0, because of PIC's memory map

; system architecture constants
SCYCLE	equ	10	; multiples of TMR2 interrupt for each pad scan
NPAD	equ	12	; number of pads
NBYTE	equ	6	; number of bytes for each pad

	org	0
	goto	init

; interrupt (TMR2) routine
	org	4
; context saving
	movwf	W_TMP
	swapf	status, 0
	movwf	S_TMP

	bsf	rp0
	decfsz	SCOUNT, 1
	goto	iend

	movlw	0ffh
	movwf	SFLAG
	movlw	SCYCLE
	movwf	SCOUNT

iend
	bcf	rp0
	bcf	tmr2if
; context recovering
	swapf	S_TMP, 0
	movwf	status
	swapf	W_TMP, 1
	swapf	W_TMP, 0

	retfie

; ----
; communicate with PADs; send CMD and receive DAT1-3
; ----
padcom
	movlw	8
	movwf	COUNT
	clrf	CDAT[0]
	clrf	CDAT[1]
	clrf	CDAT[2]
comloop
	bcf	PCLK
	call	ccwait
	btfsc	CCMD, 0
	bsf	PCMD
	btfss	CCMD, 0
	bcf	PCMD
	rrf	CCMD, 1

	rrf	CDAT[0], 1
	btfsc	PDAT1
	bsf	CDAT[0], 7
	btfss	PDAT1
	bcf	CDAT[0], 7

	rrf	CDAT[1], 1
	btfsc	PDAT2
	bsf	CDAT[1], 7
	btfss	PDAT2
	bcf	CDAT[1], 7

	rrf	CDAT[2], 1
	btfsc	PDAT3
	bsf	CDAT[2], 7
	btfss	PDAT3
	bcf	CDAT[2], 7

	bsf	PCLK
	call	ccwait
	decfsz	COUNT, 1
	goto	comloop
; wait for 100us (ACK time)
	movlw	125
	movwf	TMP
w100lp	nop
	decfsz	TMP, 1
	goto	w100lp
	return
ccwait	movlw	10
	movwf	TMP
ccwait0	decfsz	TMP, 1
	goto	ccwait0
	return

; ----
; subroutine of scanning status of all PADs
; ----
scanpad
	bcf	PSEL

; for MultiTap

	movlw	01h
	movwf	CCMD
	call	padcom

	movlw	42h
	movwf	CCMD
	call	padcom

	movlw	01h
	movwf	CCMD
	call	padcom

; for PAD.A via MultiTap

	movlw	42h
	movwf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADID[0]
	movf	CDAT[1], 0
	movwf	PADID[4]
	movf	CDAT[2], 0
	movwf	PADID[8]

	clrf	CCMD
	call	padcom

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADA[0]
	movf	CDAT[1], 0
	movwf	PADA[4]
	movf	CDAT[2], 0
	movwf	PADA[8]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADB[0]
	movf	CDAT[1], 0
	movwf	PADB[4]
	movf	CDAT[2], 0
	movwf	PADB[8]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADC[0]
	movf	CDAT[1], 0
	movwf	PADC[4]
	movf	CDAT[2], 0
	movwf	PADC[8]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADD[0]
	movf	CDAT[1], 0
	movwf	PADD[4]
	movf	CDAT[2], 0
	movwf	PADD[8]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADE[0]
	movf	CDAT[1], 0
	movwf	PADE[4]
	movf	CDAT[2], 0
	movwf	PADE[8]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADF[0]
	movf	CDAT[1], 0
	movwf	PADF[4]
	movf	CDAT[2], 0
	movwf	PADF[8]

; for PAD.B via MultiTap

	movlw	42h
	movwf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADID[1]
	movf	CDAT[1], 0
	movwf	PADID[5]
	movf	CDAT[2], 0
	movwf	PADID[9]

	clrf	CCMD
	call	padcom

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADA[1]
	movf	CDAT[1], 0
	movwf	PADA[5]
	movf	CDAT[2], 0
	movwf	PADA[9]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADB[1]
	movf	CDAT[1], 0
	movwf	PADB[5]
	movf	CDAT[2], 0
	movwf	PADB[9]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADC[1]
	movf	CDAT[1], 0
	movwf	PADC[5]
	movf	CDAT[2], 0
	movwf	PADC[9]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADD[1]
	movf	CDAT[1], 0
	movwf	PADD[5]
	movf	CDAT[2], 0
	movwf	PADD[9]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADE[1]
	movf	CDAT[1], 0
	movwf	PADE[5]
	movf	CDAT[2], 0
	movwf	PADE[9]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADF[1]
	movf	CDAT[1], 0
	movwf	PADF[5]
	movf	CDAT[2], 0
	movwf	PADF[9]

; for PAD.C via MultiTap

	movlw	42h
	movwf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADID[2]
	movf	CDAT[1], 0
	movwf	PADID[6]
	movf	CDAT[2], 0
	movwf	PADID[10]

	clrf	CCMD
	call	padcom

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADA[2]
	movf	CDAT[1], 0
	movwf	PADA[6]
	movf	CDAT[2], 0
	movwf	PADA[10]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADB[2]
	movf	CDAT[1], 0
	movwf	PADB[6]
	movf	CDAT[2], 0
	movwf	PADB[10]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADC[2]
	movf	CDAT[1], 0
	movwf	PADC[6]
	movf	CDAT[2], 0
	movwf	PADC[10]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADD[2]
	movf	CDAT[1], 0
	movwf	PADD[6]
	movf	CDAT[2], 0
	movwf	PADD[10]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADE[2]
	movf	CDAT[1], 0
	movwf	PADE[6]
	movf	CDAT[2], 0
	movwf	PADE[10]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADF[2]
	movf	CDAT[1], 0
	movwf	PADF[6]
	movf	CDAT[2], 0
	movwf	PADF[10]

; for PAD.D via MultiTap

	movlw	42h
	movwf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADID[3]
	movf	CDAT[1], 0
	movwf	PADID[7]
	movf	CDAT[2], 0
	movwf	PADID[11]

	clrf	CCMD
	call	padcom

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADA[3]
	movf	CDAT[1], 0
	movwf	PADA[7]
	movf	CDAT[2], 0
	movwf	PADA[11]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADB[3]
	movf	CDAT[1], 0
	movwf	PADB[7]
	movf	CDAT[2], 0
	movwf	PADB[11]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADC[3]
	movf	CDAT[1], 0
	movwf	PADC[7]
	movf	CDAT[2], 0
	movwf	PADC[11]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADD[3]
	movf	CDAT[1], 0
	movwf	PADD[7]
	movf	CDAT[2], 0
	movwf	PADD[11]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADE[3]
	movf	CDAT[1], 0
	movwf	PADE[7]
	movf	CDAT[2], 0
	movwf	PADE[11]

	clrf	CCMD
	call	padcom
	movf	CDAT[0], 0
	movwf	PADF[3]
	movf	CDAT[1], 0
	movwf	PADF[7]
	movf	CDAT[2], 0
	movwf	PADF[11]

	bsf	PSEL
; wait for 16ms
	movlw	80
	movwf	CCMD
w16lp1	movlw	200
	movwf	TMP
w16lp2	nop
	decfsz	TMP, 1
	goto	w16lp2
	nop
	decfsz	CCMD, 1
	goto	w16lp1
	return

; ----
; send 1 byte in TMP via USART, and wait until it finishes
; ----
tx1data
	movf	TMP, 0
	movwf	txreg	; transmit one byte
	bsf	rp0
txwloop	btfss	trmt
	goto	txwloop
	bcf	rp0
	return

; ----
; initialization of ports, USART, etc.
; ----
init
	bsf	rp0
	movlw	00h
	movwf	trisa
	movlw	7fh
	movwf	trisb
	movlw	80h
	movwf	trisc
	bsf	csrc
	bsf	brgh	; high baud rate mode

	bcf	rp0
	btfsc	portb, 6
	goto	set1152

	bsf	rp0
	movlw	32	; 32 : 38400bps@f=20MHz
	goto	cont

set1152	bsf	rp0
	movlw	10	; 10 : 115200bps@f=20MHz

cont	movwf	spbrg
	bsf	txen	; enable TX
	bcf	rbpu	; enable pull-up for PORTB
	movlw	06h
	movwf	adcon1
	bcf	rp0
	bsf	spen	; enable USART
	bsf	PSEL
	bsf	PCMD
	bsf	PCLK

; initialization of TMR2
	movlw	7ah	; 1/16 for post-scaler, 1/16 for pre-scaler
	movwf	t2con	; Fosc/4=0.2us, 0.2us*16=3.2us, 3.2us*195=624us
			; 624us*16=9.984ms (= cycle of TMR2 interrupt)
	bsf	rp0
	clrf	SFLAG
	movlw	SCYCLE
	movwf	SCOUNT
	movlw	195
	movwf	pr2
	bsf	tmr2ie	; enable TMR2 interrupt
	bcf	rp0

	clrf	tmr2

	bsf	peie	; enable peripheral interrupt

; ----
; scan pads at first, and copy their status as the previous status
; ----
	call	scanpad

	movlw	NPAD*NBYTE
	movwf	COUNT
	movlw	PADA[0]
	movwf	FSR1
	movlw	PADA0[0]
	movwf	FSR0
cploop
	movf	FSR1, 0
	movwf	fsr
	movf	indf, 0
	movwf	TMP
	movf	FSR0, 0
	movwf	fsr
	movf	TMP, 0
	bsf	rp0
	movwf	indf
	bcf	rp0
	incf	FSR1, 1
	incf	FSR0, 1
	decfsz	COUNT, 1
	goto	cploop

	bsf	gie	; enable global interrupt
	bsf	tmr2on	; start TMR2

; ---
; begining of main loop
; ---
main
	bsf	rp0

stloop
	btfss	SFLAG, 0
	goto	stloop

	clrf	SFLAG

	bcf	rp0


	call	scanpad
; ----
; finding changes of PAD status for all PADs
; if any change is found, send the PAD values of 7 bytes via USART and
; update the previous status value
; ----

; scan the change of all pad status
	movlw	0
	movwf	CCOUNT
scloop
	movlw	PADA[0]
	movwf	FSR1
	movlw	PADA0[0]
	movwf	FSR0
	movf	CCOUNT, 0
	addwf	FSR1, 1
	addwf	FSR0, 1
	movlw	NBYTE
	movwf	NSTAT
scloop2
	movf	FSR1, 0
	movwf	fsr
	movf	indf, 0
	movwf	TMP
	movf	FSR0, 0
	movwf	fsr
	bsf	rp0
	movf	indf, 0
	bcf	rp0
	subwf	TMP, 0
	btfss	z
	goto	changed
	movlw	NPAD
	addwf	FSR1, 1
	addwf	FSR0, 1
	decfsz	NSTAT, 1
	goto	scloop2
	goto	nochg
changed
; ----
; in case of status change of PAD[CCOUNT], 
; send PAD#&type, PADA[] - PADF[] via USART
; ----
	movlw	PADID[0]
	movwf	fsr
	movf	CCOUNT, 0
	addwf	fsr, 1
	movf	indf, 0
	andlw	0f0h
	movwf	TMP	; TMP[7:4] is PAD type
	movf	CCOUNT, 0
	andlw	00fh	; W[3:0] is PAD# number
	iorwf	TMP, 1
	call	tx1data

	movlw	0
	movwf	NSTAT
	movlw	PADA[0]
	movwf	fsr
	movf	CCOUNT, 0
	addwf	fsr, 1
txdloop
	movf	indf, 0
	movwf	TMP
	call	tx1data
	movlw	NPAD
	addwf	fsr, 1
	incf	NSTAT, 1
	movlw	NBYTE
	subwf	NSTAT, 0
	btfss	z
	goto	txdloop
; ----
; copy current pad status as previous pad status when any change occured
; ----
	movlw	NBYTE
	movwf	NSTAT
	movlw	PADA[0]
	movwf	FSR1

	movlw	PADA0[0]
	movwf	FSR0

	movf	CCOUNT, 0
	addwf	FSR1, 1
	addwf	FSR0, 1
cploop2
	movf	FSR1, 0
	movwf	fsr
	movf	indf, 0
	movwf	TMP
	movf	FSR0, 0
	movwf	fsr
	movf	TMP, 0
	bsf	rp0
	movwf	indf
	bcf	rp0
	movlw	NPAD
	addwf	FSR1, 1
	addwf	FSR0, 1
	decfsz	NSTAT, 1
	goto	cploop2
nochg	incf	CCOUNT, 1
	movlw	NPAD
	subwf	CCOUNT, 0
	btfss	z
	goto	scloop

	goto	main
