-
-
Save pyroesp/b088bb76131b981d15bf9718ccc29edc to your computer and use it in GitHub Desktop.
Playstation 1 reset mod for PIC16F18325
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* File: main.c | |
* Author: pyroesp | |
* | |
* Playstation 1 Reset Mod for PIC16F18325 | |
* | |
* Created on September 21, 2019, 5:33 PM | |
*/ | |
// CONFIG1 | |
#pragma config FEXTOSC = OFF // FEXTOSC External Oscillator mode Selection bits (HS (crystal oscillator) above 4 MHz) | |
#pragma config RSTOSC = HFINT32 // Power-up default value for COSC bits (HFINTOSC with 2x PLL (32MHz)) | |
#pragma config CLKOUTEN = OFF // Clock Out Enable bit (CLKOUT function is disabled; I/O or oscillator function on OSC2) | |
#pragma config CSWEN = ON // Clock Switch Enable bit (Writing to NOSC and NDIV is allowed) | |
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled) | |
// CONFIG2 | |
#pragma config MCLRE = ON // Master Clear Enable bit (MCLR/VPP pin function is MCLR; Weak pull-up enabled) | |
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled) | |
#pragma config WDTE = OFF // Watchdog Timer Enable bits (WDT disabled; SWDTEN is ignored) | |
#pragma config LPBOREN = OFF // Low-power BOR enable bit (ULPBOR disabled) | |
#pragma config BOREN = OFF // Brown-out Reset Enable bits (Brown-out Reset disabled) | |
#pragma config BORV = LOW // Brown-out Reset Voltage selection bit (Brown-out voltage (Vbor) set to 2.45V) | |
#pragma config PPS1WAY = ON // PPSLOCK bit One-Way Set Enable bit (The PPSLOCK bit can be cleared and set only once; PPS registers remain locked after one clear/set cycle) | |
#pragma config STVREN = OFF // Stack Overflow/Underflow Reset Enable bit (Stack Overflow or Underflow will not cause a Reset) | |
#pragma config DEBUG = OFF // Debugger enable bit (Background debugger disabled) | |
// CONFIG3 | |
#pragma config WRT = OFF // User NVM self-write protection bits (Write protection off) | |
#pragma config LVP = OFF // Low Voltage Programming Enable bit (High Voltage on MCLR/VPP must be used for programming.) | |
// CONFIG4 | |
#pragma config CP = OFF // User NVM Program Memory Code Protection bit (User NVM code protection disabled) | |
#pragma config CPD = OFF // Data NVM Memory Code Protection bit (Data NVM code protection disabled) | |
// #pragma config statements should precede project file includes. | |
// Use project enums instead of #define for ON and OFF. | |
#define _XTAL_FREQ 32000000 | |
#include <xc.h> | |
#include <stdint.h> | |
#define _BV(b) (1<<(b)) | |
#define REBOOT_DELAY 2 // 30 seconds | |
#define PS1_LAT_IO LATC // IO port reg used for RESET | |
#define PS1_TRIS_IO TRISC // IO dir reg used for RESET | |
#define PS1_RESET 5 // Only IO that outputs a logic 0 | |
/* Controller ID */ | |
#define ID_DIG_CTRL 0x5A41 // digital | |
#define ID_ANP_CTRL 0x5A73 // analog/pad | |
#define ID_ANS_CTRL 0x5A53 // analog/stick | |
#define ID_GUNCON_CTRL 0x5A63 // light gun | |
/* PlayStation Commands */ | |
#define CMD_SEL_CTRL_1 0x01 // select controller 1 | |
#define CMD_SEL_MEMC_1 0x81 // select memory card 1 | |
#define CMD_READ_SW 0x42 // read switch status from controller | |
/* PlayStation Buff Size */ | |
#define PS1_CTRL_BUFF_SIZE 9 // max size of buffer needed for a controller | |
/* Key Combo */ | |
#define KEY_COMBO_CTRL 0xFCF6 // select-start-L2-R2 1111 1100 1111 0110 | |
#define KEY_COMBO_GUNCON 0x9FF7 // A-trigger-B 1001 1111 1111 0111 | |
/* PlayStation Controller Command Union */ | |
union PS1_Cmd{ | |
uint8_t buff[PS1_CTRL_BUFF_SIZE]; | |
struct{ | |
uint8_t device_select; // 0x01 or 0x81 | |
uint8_t command; // 0x42 for read switch | |
uint8_t unused[PS1_CTRL_BUFF_SIZE-2]; // always 0 for controller | |
}; | |
}; | |
/* PlayStation Controller Data Union */ | |
union PS1_Ctrl_Data{ | |
uint8_t buff[PS1_CTRL_BUFF_SIZE]; // buffer to read data | |
struct{ | |
uint8_t unused; // always 0xFF | |
uint16_t id; // 0x5Ayz - y = type; z = # of half word | |
uint16_t switches; // controller switches | |
union{ | |
// light gun only (8MHz clock counter since hsync) | |
uint16_t x_pos; // if 0x0001 then error, check y_pos | |
struct{ | |
uint8_t adc0; // right joy X | |
uint8_t adc1; // right joy Y | |
}; | |
}; | |
union{ | |
//light gun only (scanlines since vsync) | |
uint16_t y_pos; // if x_pos = 0x0001 && y_pos = 0x000A => not aimed at screen | |
struct{ | |
uint8_t adc2; // left joy X | |
uint8_t adc3; // left joy Y | |
}; | |
}; | |
}; | |
}; | |
void clear_buff(uint8_t *p, uint8_t s); | |
void __delay_s(uint8_t s); | |
volatile union PS1_Cmd cmd; | |
volatile union PS1_Ctrl_Data data; | |
volatile uint8_t cmd_cnt, data_cnt; | |
// SPI interrupt cmd | |
void __interrupt() _spi_int(void) { | |
LATAbits.LATA5 ^= 1; // toggle PIN A5 | |
if (PIR0bits.TMR0IF){ | |
TMR0L = 0xF0; | |
TMR0H = 0xFF; | |
PIR0bits.TMR0IF = 0; | |
} | |
if (PIR1bits.SSP1IF){ | |
cmd.buff[cmd_cnt] = SSP1BUF; | |
cmd_cnt++; | |
PIR1bits.SSP1IF = 0; // clear SPI1 flag | |
} | |
if (PIR2bits.SSP2IF){ | |
data.buff[data_cnt] = SSP2BUF; | |
data_cnt++; | |
PIR2bits.SSP2IF = 0; // clear SPI2 flag | |
} | |
return; | |
} | |
/* | |
* SS2 : RA0 | |
* SCK2 : RA1 | |
* SDI2 : RA2 | |
* | |
* SS1 : RC0 | |
* SCK1 : RC1 | |
* SDI1 : RC2 | |
* | |
* SDO2 : RC4 | |
* SDO1 : RC3 | |
*/ | |
void main(void){ | |
// SETUP I/O | |
LATC = 0; | |
LATA = 0; | |
PS1_LAT_IO &= ~_BV(PS1_RESET); // clear reset output | |
PS1_TRIS_IO |= _BV(PS1_RESET); // set reset pin to input | |
TRISAbits.TRISA0 = 1; // SS2 set to input | |
TRISAbits.TRISA1 = 1; // SCK2 set to input | |
TRISAbits.TRISA2 = 1; // SDI2 set to input | |
TRISCbits.TRISC0 = 1; // SS1 set to input | |
TRISCbits.TRISC1 = 1; // SCK1 set to input | |
TRISCbits.TRISC2 = 1; // SDI1 set to input | |
TRISCbits.TRISC3 = 0; // SDO2 set to output | |
TRISCbits.TRISC4 = 0; // SDO1 set to output | |
// SETUP SPI1 & SPI2 I/O | |
SSP1SSPPSbits.SSP1SSPPS = 0x10; // SS1 = RC0 | |
SSP1CLKPPSbits.SSP1CLKPPS = 0x11; // SCK1 = RC1 | |
SSP1DATPPSbits.SSP1DATPPS = 0x12; // SDI1 = RC2 | |
SSP2SSPPSbits.SSP2SSPPS = 0x00; // SS2 = RA0 | |
SSP2CLKPPSbits.SSP2CLKPPS = 0x01; // SCK2 = RA1 | |
SSP2DATPPSbits.SSP2DATPPS = 0x02; // SDI2 = RA2 | |
RC4PPSbits.RC4PPS = 0x1B; // SDO2 = RC4 | |
RC3PPSbits.RC3PPS = 0x19; // SDO1 = RC3 | |
// SETUP SPI1 & SPI2 | |
SSP1STATbits.SMP = 0; // slave mode SPI1 | |
SSP1STATbits.CKE = 1; // transmit from active to idle | |
SSP1CON1bits.CKP = 1; // clock idle high | |
SSP1CON1bits.SSPM = 0x04; // slave mode: clk = sck pin; SS enabled | |
SSP1CON3bits.BOEN = 1; // ignore BF flag | |
SSP1CON1bits.SSPEN = 1; // enable SPI1 | |
SSP2STATbits.SMP = 0; // slave mode SPI2 | |
SSP2STATbits.CKE = 1; // transmit from active to idle | |
SSP2CON1bits.CKP = 1; // clock idle = high | |
SSP2CON1bits.SSPM = 0x04; // slave mode: clk = sck pin; SS enabled | |
SSP2CON3bits.BOEN = 1; // ignore BF flag | |
SSP2CON1bits.SSPEN = 1; // enable SPI2 | |
SSP1BUF = 0xFF; | |
SSP2BUF = 0xFF; | |
// DEBUG setup here | |
ANSELAbits.ANSA5 = 0; // set pin to digital I/O | |
TRISAbits.TRISA5 = 0; // set pin to output | |
LATAbits.LATA5 = 1; // set output high | |
ANSELAbits.ANSA4 = 0; // set pin to digital I/O | |
TRISAbits.TRISA4 = 0; // set pin to output | |
LATAbits.LATA4 = 1; // set output high | |
RA4PPSbits.RA4PPS = 0x00; | |
RA5PPSbits.RA5PPS = 0x00; | |
// SETUP variables and arrays | |
data_cnt = 0; | |
cmd_cnt = 0; | |
clear_buff((uint8_t*)cmd.buff, PS1_CTRL_BUFF_SIZE); | |
clear_buff((uint8_t*)data.buff, PS1_CTRL_BUFF_SIZE); | |
// delay before enabling interrupts | |
__delay_s(REBOOT_DELAY); // wait 30 sec before next reset, this is to prevent multiple reset one after the other | |
// SETUP INTERRUPTS | |
PIR1bits.SSP1IF = 0; // clear SPI1 flag | |
PIR2bits.SSP2IF = 0; // clear SPI2 flag | |
PIE1bits.SSP1IE = 1; // enable MSSP interrupt (SPI1) | |
PIE2bits.SSP2IE = 1; // enable MSSP interrupt (SPI2) | |
T0CON0bits.T0EN = 1; // enable timer0 module | |
T0CON0bits.T016BIT = 1; // 16 bit mode timer0 | |
T0CON1bits.T0ASYNC = 1; // not synched to Fosc/4 | |
T0CON1bits.T0CS = 0x03; // clock source HFINTOSC | |
TMR0L = 0xF0; | |
TMR0H = 0xFF; | |
PIR0bits.TMR0IF = 0; // clear timer0 flag | |
PIE0bits.TMR0IE = 1; // enable timer0 interrupt | |
INTCONbits.PEIE = 1; // peripheral interrupt enable | |
INTCONbits.GIE = 1; // global interrupt enable | |
// MAIN LOOP | |
for(;;){ | |
LATAbits.LATA4 ^= 1; // toggle output | |
if (data_cnt >= PS1_CTRL_BUFF_SIZE || cmd_cnt >= PS1_CTRL_BUFF_SIZE){ | |
INTCONbits.PEIE = 0; // global interrupt disable | |
uint16_t key_combo = 0; | |
// Check first command for device selected | |
if (cmd.device_select == CMD_SEL_CTRL_1 && cmd.command == CMD_READ_SW){ | |
// Check ID | |
switch(data.id){ | |
case ID_GUNCON_CTRL: | |
key_combo = KEY_COMBO_GUNCON; | |
break; | |
case ID_DIG_CTRL: | |
case ID_ANP_CTRL: | |
case ID_ANS_CTRL: | |
key_combo = KEY_COMBO_CTRL; | |
break; | |
default: | |
key_combo = 0; | |
break; | |
} | |
// Check switch combo | |
if (key_combo != 0 && 0 == (data.switches ^ key_combo)){ | |
// change RESET from input to output, logic low (PORT is already 0) | |
PS1_TRIS_IO &= ~_BV(PS1_RESET); | |
__delay_ms(100); // hold reset for 100ms | |
// change RESET back to input | |
PS1_TRIS_IO = _BV(PS1_RESET); | |
__delay_s(REBOOT_DELAY); // wait 30 sec before next reset, this is to prevent multiple reset one after the other | |
} | |
} | |
clear_buff((uint8_t*)data.buff, PS1_CTRL_BUFF_SIZE); // clear controller stuff | |
clear_buff((uint8_t*)cmd.buff, PS1_CTRL_BUFF_SIZE); // clear controller stuff | |
data_cnt = 0; | |
cmd_cnt = 0; | |
__delay_ms(50); | |
PIR1bits.SSP1IF = 0; // clear SPI1 flag | |
PIR2bits.SSP2IF = 0; // clear SPI2 flag | |
INTCONbits.PEIE = 1; // global interrupt enable | |
} | |
} | |
for(;;); | |
return; | |
} | |
void clear_buff(uint8_t *p, uint8_t s){ | |
uint8_t i; | |
for (i = 0; i < s; i++) | |
p[i] = 0; | |
} | |
void __delay_s(uint8_t s){ | |
for (; s > 0; s--) | |
__delay_ms(1000); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment