;***************************************************************************************************;
;																									;
;	Author: Daniel Hearn																			;
;	Filename: transmitter.asm																		;
;	Version: 1.1																					;
;	Date: 03-28-2007																				;
;	PIC type: PIC16F84A																				;
;	Clock Speed: 4 mHz																				;
;																									;
;***************************************************************************************************;
;																									;
;	Brief Description: Interfaces a 3 button Sega Genesis controller with the wireless reciever by  ;
;		Mark Feldman.  The transmission "send_loop" and delay "pause" routines were written by Mark ;
;		Feldman.  I only wrote the controller sequence "main_loop".  								;
;																									;
;	Copyright:	"send_loop" & "pause" Copyright (c) 2007 Mark Feldman. All Rights Reserved.			;
;				"main_loop" Copyright (c) 2007 J Daniel Hearn. All Rights Reservered				;
;																									;																								;
;***************************************************************************************************;


	list	p=16F84A
	radix	hex

	#include "C:\Program Files\Microchip\MPASM Suite\p16f84a.inc"

	__config	_HS_OSC & _PWRTE_OFF & _WDT_ON & _CP_OFF

;********** Constants ******************************************************************************;

NUM_BUTTONS		equ	0Ch

CONTROLLER_PORT equ PORTB
BUTTON_UP		equ 0
BUTTON_DOWN		equ 1
BUTTON_LEFT_0	equ 2
BUTTON_RIGHT_0	equ 3
BUTTON_B_A		equ 4
BUTTON_C_START	equ 5

SELECT_PORT		equ PORTA
SELECT_BIT		equ 3

WIRELESS_PORT	equ	PORTA
WIRELESS_BIT	equ	1

;********* Variables *******************************************************************************;

TIMER1			equ	0Ch							; misc variables used by the routines
TIMER2			equ	0Dh
CURBIT			equ	0Eh

START_BIT		equ	0Fh							; this must be on an odd address to assist ASK-friendly encoding
BUTTON_BITS		equ (START_BIT+1)
STOP_BIT		equ (BUTTON_BITS+NUM_BUTTONS)

	org	0000h

;********* Program Initialization ******************************************************************;

init
		bsf		STATUS, RP0						; select bank 1
		bcf		WIRELESS_PORT,  WIRELESS_BIT	; wireless  bit is an output
		bcf		SELECT_PORT, SELECT_BIT			; select bit is an output
		movlw	0FFh							; controller port is an input
		movwf	TRISB
		bcf		STATUS, RP0						; select bank 0

		clrf	START_BIT						; set packet start bit to 0 (gets negated during output)
		clrf	STOP_BIT						; set packet stop bit to 1 (doesn't get negated during output)
		bsf		STOP_BIT, WIRELESS_BIT
		bcf		WIRELESS_PORT, WIRELESS_BIT		; default wireless state is 0

;********* Controller Read Sequence ****************************************************************;
;		
; This section reads the inputs in from the sega genesis controller.  The select line must be high
; to read buttons B, C, Left, Right.  It should be low to read A and C.  It does not affect Up and 
; Down.  If an input is low its corresponding button is being pressed.  The button states are put
; into the array according to how they map to a gamecube controller. I.E. A = B, B = A, C = X
;
;***************************************************************************************************;

main_loop										
 		movlw	BUTTON_BITS						; point FSR to the location of the button states array
		movwf	FSR
		
		bcf		SELECT_PORT, SELECT_BIT			; sets select line low to read A
		clrf	INDF							; clear destination bit (SNES/GC B)
		btfss	CONTROLLER_PORT, BUTTON_B_A		; read the button state, is it a 0?
		bsf		INDF, WIRELESS_BIT				; if yes then set destination bit
		nop										; else leave it cleared
		incf	FSR, F							; move the array ptr to the next entry
		

		nop										; skip this button in in the array because nothing maps to it
		clrf	INDF							; clear destination bit (SNES/GC Y)
		nop
		nop
		nop
		incf	FSR, F							; move the array ptr to the next entry


		nop										; skip this button in in the array because nothing maps to it
		clrf	INDF							; clear destination bit (SNES Select)
		nop
		nop
		nop
		incf	FSR, F							; move the array ptr to the next entry


		bcf		SELECT_PORT, SELECT_BIT			; sets select line low to read Start
		clrf	INDF							; clear destination bit (SNES/CG Start)
		btfss	CONTROLLER_PORT, BUTTON_C_START	; read the button state, is it a 0?
		bsf		INDF, WIRELESS_BIT				; if yes then set destination bit
		nop										; else leave it cleared
		incf	FSR, F							; move the array ptr to the next entry


		nop										; select line does not matter for Up
		clrf	INDF							; clear destination bit (Up)
		btfss	CONTROLLER_PORT, BUTTON_UP		; read the button state, is it a 0?
		bsf		INDF, WIRELESS_BIT				; if yes then set destination bit
		nop										; else leave it cleared
		incf	FSR, F							; move the array ptr to the next entry


		nop										; select line does not matter for Down
		clrf	INDF							; clear destination bit (Down)
		btfss	CONTROLLER_PORT, BUTTON_DOWN	; read the button state, is it a 0?
		bsf		INDF, WIRELESS_BIT				; if yes then set destination bit
		nop										; else leave it cleared
		incf	FSR, F							; move the array ptr to the next entry


		bsf		SELECT_PORT, SELECT_BIT			; set select line high for Left
		clrf	INDF							; clear destination bit (Left)
		btfss	CONTROLLER_PORT, BUTTON_LEFT_0	; read the button state, is it a 0?
		bsf		INDF, WIRELESS_BIT				; if yes then set destination bit
		nop										; else leave it cleared
		incf	FSR, F							; move the array ptr to the next entry


		bsf		SELECT_PORT, SELECT_BIT			; set select line high for Right
		clrf	INDF							; clear destination bit (Right)
		btfss	CONTROLLER_PORT, BUTTON_RIGHT_0	; read the button state, is it a 0?
		bsf		INDF, WIRELESS_BIT				; if yes then set destination bit
		nop										; else leave it cleared
		incf	FSR, F							; move the array ptr to the next entry


		bsf		SELECT_PORT, SELECT_BIT			; set select line high for B
		clrf	INDF							; clear destination bit (SNES/GC A)
		btfss	CONTROLLER_PORT, BUTTON_B_A		; read the button state, is it a 0?
		bsf		INDF, WIRELESS_BIT				; if yes then set destination bit
		nop										; else leave it cleared
		incf	FSR, F							; move the array ptr to the next entry


		bsf		SELECT_PORT, SELECT_BIT			; set select line high for C
		clrf	INDF							; clear destination bit (SNES/GC X)
		btfss	CONTROLLER_PORT, BUTTON_C_START	; read the button state, is it a 0?
		bsf		INDF, WIRELESS_BIT				; if yes then set destination bit
		nop										; else leave it cleared
		incf	FSR, F


		nop										; skip this button in in the array because nothing maps to it
		clrf	INDF							; clear destination bit (SNES/GC L)
		nop
		nop
		nop
		incf	FSR, F							; move the array ptr to the next entry


		nop										; skip this button in in the array because nothing maps to it
		clrf	INDF							; clear destination bit (SNES/GC R)
		nop
		nop
		nop
		incf	FSR, F							; move the array ptr to the next entry



;********* Transmission Routine ********************************************************************;\

transmit_packet
		movlw	START_BIT						; point FSR to the location of the button states
		movwf	FSR
		movlw   NUM_BUTTONS+2					; 1 start bit + 12 data bits + 1 stop bit
		movwf   CURBIT
send_loop
		movfw	WIRELESS_PORT					; grab the port state
		andlw	~(1 << WIRELESS_BIT)			; clear the send bit
		iorwf	INDF, W							; move the bit from the buffer into W
		btfsc	FSR, 0							; change polarity of each bit for ASK-friendly encoding
		xorlw	1 << WIRELESS_BIT
		movwf	PORTA							; send it to the transmitter
		incf	FSR, F							; move to the next position
		movlw   4Eh								; hard-coded delay to help pad the pulse out to 250 cycles
        call    delay							; keep the line like this for the duration of the pulse
		nop
		decfsz	CURBIT, F						; any more bits to send?
		goto	send_loop						; yep, so loop back
		bcf		WIRELESS_PORT, WIRELESS_BIT		; pad the 3.5mS between packets with 0s

;********* Delay Routine *****************************************************************************;
;
; the length of the wireless packet is exactly 3.5ms/3500 cycles, so we now need to pad it out with 
; enough 0s to make the main loop exactly 7ms/7000 cycles. i would have liked to put the chip to sleep 
; for this to save power, but clock interrupts are disabled during sleep.
;
;*****************************************************************************************************;

pause
		movlw   0ffh
		call    delay
		movlw   0ffh
		call    delay
		movlw   0ffh
		call    delay
		movlw   0ffh
		call    delay
		movlw   06fh
		call    delay
		nop
		clrwdt									; let the watchdog timer know I'm still awake
		goto	main_loop						; all done


delay   movwf   TIMER1							; this routine causes a delay of (4 + W * 3) instructions
dloop   decfsz  TIMER1,f						; (ie. uS), including the call here and the return
        goto    dloop
        return

		end
