; ================================================================================================ ; PROJECT: SnesHack - A wireless/Wii-enabled SNES controller hack. ; FILENAME: Transmitter.asm ; DESCRIPTION: Code for the 16F84A firmware in the SNES controller. ; COPYRIGHT: Copyright (c) 2007 Mark Feldman. All Rights Reserved. ; ================================================================================================ 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 ; clocks the data out of the controller CLOCK_PORT equ PORTB CLOCK_BIT equ 0 ; signals the controller to latch all button states LATCH_PORT equ PORTB LATCH_BIT equ 1 ; reads the clocked data from the controller DATA_PORT equ PORTB DATA_BIT equ 2 ; sends a packet of button press data to the transmitter chip 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 ; ================================================================================================ ; entry point - performs initialization ; ================================================================================================ start bsf STATUS, RP0 ; select bank 1 bcf WIRELESS_PORT, WIRELESS_BIT ; wireless bit is an output bcf LATCH_PORT, LATCH_BIT ; latch bit is an output bcf CLOCK_PORT, CLOCK_BIT ; clock bit is an output bsf DATA_PORT, DATA_BIT ; data bit is an input 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 LATCH_PORT, LATCH_BIT ; default latch state is 0 bsf CLOCK_PORT, CLOCK_BIT ; default clock state is 1 bcf WIRELESS_PORT, WIRELESS_BIT ; default wireless state is 0 main_loop ; read the button states from the controller bsf LATCH_PORT, LATCH_BIT ; latch = 1 maintain for 12us movlw 2 call delay bcf LATCH_PORT, LATCH_BIT ; latch = 0 maintain for 6us movlw BUTTON_BITS ; point FSR to the location of the button states array movwf FSR movlw NUM_BUTTONS ; set up the bit counter movwf CURBIT nop read_loop bcf CLOCK_PORT, CLOCK_BIT ; clock = 0, maintain for 6uS clrf INDF ; clear destination bit btfss DATA_PORT, DATA_BIT ; read the button state, is it a 0? bsf INDF, WIRELESS_BIT ; if yes then set destination bit nop incf FSR, F ; move the array ptr to the next entry bsf CLOCK_PORT, CLOCK_BIT ; clock = 1, maintain for 6uS nop nop decfsz CURBIT, F ; any more bits to read? goto read_loop ; yep, so loop back ; send the packet to the transmitter 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 ; 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 052h call delay nop clrwdt ; let the watchdog timer know I'm still awake goto main_loop ; all done ; this routine causes a delay of (4 + W * 3) instructions (ie. uS), including the call here and the return delay movwf TIMER1 dloop decfsz TIMER1,f goto dloop return end