mirror of
https://github.com/kalymos/PsNee.git
synced 2026-03-09 20:38:21 +00:00
Update PsNee.ino
This commit is contained in:
382
PsNee.ino
382
PsNee.ino
@@ -1,102 +1,4 @@
|
||||
//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||
//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||
// PPPPPPPPPPPPPPPP P P
|
||||
// P P PP P
|
||||
// P P P P P
|
||||
// P P P P P
|
||||
// P P P P P
|
||||
// P P P P P
|
||||
// P P P P P
|
||||
// PPPPPPPPPPPPPPPP PPPPPPPPPPP P P P PPPPPPPPPPP PPPPPPPPPPP
|
||||
// P P P P P P P
|
||||
// P P P P P P P
|
||||
// P P P P P P P
|
||||
// P P P P P P P
|
||||
// P PPPPPPPPPPPPPP P PP PPPPPPP PPPPPPP
|
||||
// P P P P P P
|
||||
// P P P P P P
|
||||
// P P P P P P
|
||||
// P P P P P P
|
||||
// P P P P P P
|
||||
// P P P P P P
|
||||
// PPPPPPPPPPPP P P PPPPPPPPPPP PPPPPPPPPPP VERSION 6!
|
||||
|
||||
//UPDATED AT MAY 14 2016, CODED BY THE FRIENDLY FRIETMAN :-)
|
||||
|
||||
//PsNee, an open source stealth modchip for the Sony Playstation 1, usable on
|
||||
//all platforms supported by Arduino, preferably ATTiny. Finally something modern!
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// TL;DR
|
||||
//--------------------------------------------------
|
||||
//Look for the "Arduino selection!" section and verify the target platform. Hook up your target device and hit Upload!
|
||||
//BEWARE: when using ATTiny45, make sure the proper device is selected (Extra=>Board=>ATTiny45 (internal 8MHz clock))
|
||||
//and the proper fuses are burnt (use Extra=>Burn bootloader for this), otherwise PsNee will malfunction. A tutorial on
|
||||
//uploading Arduino code via an Arduino Uno to an ATTiny device: http://highlowtech.org/?p=1695
|
||||
//Look at the pinout for your device and hook PsNee up to the points on your Playstation.
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// General info!
|
||||
//--------------------------------------------------
|
||||
//PLAYSTATION 1 SECURITY - HOW IT DOES IT'S THING:
|
||||
//Sony didn't really go through great lenghts to protect it's precious Playstation
|
||||
//from running unauthorised software; the main security is based on a simple ASCII
|
||||
//string of text that is read from a part of an original Playstation disc that cannot
|
||||
//be reproduced by an ordinary PC CD burner.
|
||||
//As most of you will know, a CD is basically a very long rolled up (carrier) string in which very
|
||||
//little pits and ehm... little not-pits are embedded that represent the data stored on the disc.
|
||||
//The nifty Sony engineers did not use the pits and stuff to store the security checks for
|
||||
//Playstation discs but went crazy with the rolled up carrier string. In an ordinary CD, the
|
||||
//string is rolled up so that the spacing between the tracks is as equal as possible. If that
|
||||
//is not the case, the laser itself needs to move a bit to keep track of the track and
|
||||
//reliably read the data off the disc.
|
||||
//If you wonder how the laser knows when it follows the track optimally: four photodiodes, light
|
||||
//intensity measurement, difference measurements, servo. There.
|
||||
//To the point: the Sony engineers decidedly "fumbled up" the track of sector 4 on a Playstation
|
||||
//disc (the track was modulated in nerd-speak) so that the error correction circuit outputs a
|
||||
//recognisable signal, as the laser needs to be corrected to follow the track optimally.
|
||||
//This output signal actually is a 250bps serial bitstream (with 1 start bit and 2 stop bits) which
|
||||
//in plain ASCII says SCEA (Sony Computer Entertainment of America), SCEE (Sony Computer Entertainment
|
||||
//of Europe) or SCEI (Sony Computer Entertainment of Japan), depending on the region of the disc inserted.
|
||||
//The security thus functions not only as copy protection, but also as region protection.
|
||||
//The text string from the disc is compared with the text string that is embedded in the Playstation
|
||||
//hardware. When these text strings are the same, the disc is interpreted to be authentic and from
|
||||
//the correct region. Bingo!
|
||||
|
||||
//HOW THE MODCHIP TRICKS THE PLAYSTATION:
|
||||
//The modchip isn't all that of a complicated device: clever reverse engineers found the point on the
|
||||
//Playstation motherboard that carried the text string from the disc and found a way to temporarily block
|
||||
//this signal (by grounding an input of an op-amp buffer) to be able to inject the signal from the modchip
|
||||
//The modchip injects after about 1500ms the text strings SCEE SCEA SCEI on the motherboard point and stops
|
||||
//with this after about 25 seconds. Because all the possible valid region options are outputted on the
|
||||
//motherboard the Playstation gets a bit confused and simply accepts the inserted disc as authentic; after all,
|
||||
//one of the codes was the same as that of the Playstation hardware...
|
||||
//Early modchips applied the text strings as long as power was applied to them, whereby later Playstation
|
||||
//software could detect whether a modchip was installed. This is circumvented in this application by idling the
|
||||
//modchip after about 25 seconds. The text strings are only tranmitted again when the CD lid is opened and closed
|
||||
//again, to enable playing multi-disc games. This is also called a stealth modchip in marketing-speak.
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// New in this version!
|
||||
//--------------------------------------------------
|
||||
//A lot!
|
||||
// - The PAL SCPH-102 NTSC BIOS-patch works flawlessly! For speed reasons this is implemented in bare
|
||||
// AVR C. It is functionally identical to the OneChip modchip, this modchip firmware was disassembled,
|
||||
// documented (available on request, but written in Dutch...) and analyzed with a logic analyzer to
|
||||
// make sure PsNee works just as well.
|
||||
// - The code now is segmented in functions which make the program a lot more maintable and readable
|
||||
// - Timing is perfected, all discs (both backups and originals of PAL and NTSC games) now work in the
|
||||
// PAL SCPH-102 test machine
|
||||
// - It was found out that the gate signal doesn't havbe to be hooked up to a PAL SCPH-102 Playstation
|
||||
// to circumvent the copy protection. This is not tested on other Playstation models so the signal still
|
||||
// is available
|
||||
// - The /xlat signal is no longer required to time the PAL SCPH-102 NTSC BIOS-patch
|
||||
// - Only AVR PORTB is used for compatibility reasons (almost all the AVR chips available have PORTB)
|
||||
|
||||
|
||||
// ATTYNee!!! Universal mod for PS1 & PSOne.
|
||||
//--------------------------------------------------
|
||||
// Pinouts!
|
||||
//--------------------------------------------------
|
||||
@@ -104,225 +6,219 @@
|
||||
// - Arduino pin 8 = data = ATMega pin 14
|
||||
// - Arduino pin 9 = gate = ATMega pin 15
|
||||
// - Arduino pin 10 = lid = ATMega pin 16
|
||||
// - Arduino pin 11 = biosA18 = ATMega pin 17
|
||||
// - Arduino pin 11 = biosA18 = ATMega pin 2
|
||||
// - Arduino pin 12 = biosD2 = ATMega pin 18
|
||||
|
||||
//FOR ATTINY25/45/85:
|
||||
// - Arduino pin 0 = data = ATTiny pin 5
|
||||
// - Arduino pin 1 = gate = ATTiny pin 6
|
||||
// - Arduino pin 2 = lid = ATTiny pin 7
|
||||
// - Arduino pin 3 = biosA18 = ATTiny pin 2
|
||||
// - Arduino pin 4 = biosD2 = ATTiny pin 3
|
||||
|
||||
|
||||
//FOR ATTINY13/25/45/85:
|
||||
// - PB0 = data = ATTiny pin 5
|
||||
// - PB1 = gate = ATTiny pin 6
|
||||
// - PB2 = lid = ATTiny pin 7
|
||||
// - PB3 = biosA18 = ATTiny pin 2
|
||||
// - PB4 = biosD2 = ATTiny pin 3
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
// Includes!
|
||||
//--------------------------------------------------
|
||||
#include <Flash.h>
|
||||
|
||||
//--------------------------------------------------
|
||||
// Arduino selection!
|
||||
// Chip selection!
|
||||
//--------------------------------------------------
|
||||
#define ARDUINO_UNO //Make that "#define ARDUINO_UNO" if you want to compile for Arduino Uno instead of ATTiny25/45/85
|
||||
|
||||
|
||||
#ifdef ARDUINO_UNO
|
||||
//Pins
|
||||
int data = 8; //The pin that outputs the SCEE SCEA SCEI string
|
||||
int gate = 9; //The pin that outputs the SCEE SCEA SCEI string
|
||||
int lid = 10; //The pin that gets connected to the internal CD lid signal; active high
|
||||
int biosA18 = 11; //Only used in SCPH-102 PAL mode
|
||||
int biosD2 = 12; //Only used in SCPH-102 PAL mode
|
||||
int delay_ntsc = 2350;
|
||||
int delay_between_bits = 4;
|
||||
int delay_between_injections = 74;
|
||||
unsigned char data = 8; //The pin that outputs the SCEE SCEA SCEI string
|
||||
unsigned char gate = 9; //The pin that outputs the SCEE SCEA SCEI string
|
||||
unsigned char lid = 10; //The pin that gets connected to the internal CD lid signal; active high
|
||||
unsigned char biosA18 = 11; //Only used in SCPH-102 PAL mode
|
||||
unsigned char biosD2 = 12; //Only used in SCPH-102 PAL mode
|
||||
unsigned short delay_ntsc = 2350;
|
||||
boolean injectFlag = 0;
|
||||
unsigned char delay_between_bits = 4;
|
||||
unsigned char delay_between_injections = 74;
|
||||
#endif
|
||||
|
||||
#ifdef ATTINY
|
||||
|
||||
#ifdef ATTiny25/45/85
|
||||
//Pins
|
||||
int data = 0; //The pin that outputs the SCEE SCEA SCEI string
|
||||
int gate = 1;
|
||||
int lid = 2; //The pin that gets connected to the internal CD lid signal; active high
|
||||
int biosA18 = 3; //Only used in SCPH-102 PAL mode
|
||||
int biosD2 = 4; //Only used in SCPH-102 PAL mode
|
||||
int delay_ntsc = 2400;
|
||||
int delay_between_bits = 4;
|
||||
int delay_between_injections = 68;
|
||||
unsigned char data = 0; //The pin that outputs the SCEE SCEA SCEI string
|
||||
unsigned char gate = 1;
|
||||
unsigned char lid = 2; //The pin that gets connected to the internal CD lid signal; active high
|
||||
unsigned char biosA18 = 3; //Only used in SCPH-102 PAL mode
|
||||
unsigned char biosD2 = 4; //Only used in SCPH-102 PAL mode
|
||||
unsigned short delay_ntsc = 2400;
|
||||
boolean injectFlag = 0;
|
||||
unsigned char delay_between_bits = 4;
|
||||
unsigned char delay_between_injections = 68;
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------
|
||||
// Global variables!
|
||||
//--------------------------------------------------
|
||||
//None, just like it should be!
|
||||
|
||||
//--------------------------------------------------
|
||||
// Seperate functions!
|
||||
//--------------------------------------------------
|
||||
|
||||
#ifdef ATTiny13A
|
||||
//Pins
|
||||
unsigned char data = 0; //The pin that outputs the SCEE SCEA SCEI string
|
||||
unsigned char gate = 1;
|
||||
unsigned char lid = 2; //The pin that gets connected to the internal CD lid signal; active high
|
||||
unsigned char biosA18 = 3; //Only used in SCPH-102 PAL mode
|
||||
unsigned char biosD2 = 4; //Only used in SCPH-102 PAL mode
|
||||
unsigned short delay_ntsc = 2400;
|
||||
boolean injectFlag = 0;
|
||||
unsigned char delay_between_bits = 4;
|
||||
unsigned char delay_between_injections = 72;
|
||||
#endif
|
||||
|
||||
bool readBit(int index, const unsigned char *ByteSet)
|
||||
{
|
||||
int byte_index = index >> 3;
|
||||
byte bits = pgm_read_byte(&(ByteSet[byte_index]));
|
||||
int bit_index = index & 0x7; // same as (index - byte_index<<3) or (index%8)
|
||||
byte mask = 1 << bit_index;
|
||||
return (0 != (bits & mask));
|
||||
}
|
||||
void NTSC_fix()
|
||||
{
|
||||
//Make sure all pins are inputs
|
||||
DDRB = 0x00;
|
||||
|
||||
//Wait until just before the pulse on BIOS A18 arrives
|
||||
delay(delay_ntsc);
|
||||
|
||||
//...And wait here until it actually happened
|
||||
while(!(PINB & B00001000))
|
||||
while (!(PINB & 0b00001000))
|
||||
{
|
||||
; //Wait
|
||||
}
|
||||
delayMicroseconds(12);
|
||||
PORTB = B00000000;
|
||||
DDRB = B00010000;
|
||||
PORTB = 0b00000000;
|
||||
DDRB = 0b00010000;
|
||||
delayMicroseconds(5);
|
||||
DDRB = 0x00;
|
||||
//Serial.println("NTSC_fixed!"); //Debug only
|
||||
}
|
||||
|
||||
|
||||
void inject_SCEE()
|
||||
{
|
||||
//SCEE-array // Start Data Stop
|
||||
FLASH_ARRAY (boolean, SCEEData, 1,0,0,1,1,0,1,0,1,0,0,1,0,0,1,1,1,1,0,1,0,0,1,0,1,0,1,1,1,0,1,0,0,1,0,1,0,1,1,1,0,1,0,0); //SCEE: 1 00110101 00, 1 00111101 00, 1 01011101 00, 1 01011101 00 44 bits total
|
||||
|
||||
int bit_counter;
|
||||
|
||||
for (bit_counter = 0; bit_counter < 44; bit_counter = bit_counter + 1)
|
||||
{
|
||||
if (SCEEData[bit_counter] == 0)
|
||||
{
|
||||
static const unsigned char ByteSet[] PROGMEM = {0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11101010, 0b00000010};
|
||||
//Serial.println("inject_SCEE"); //Debug only
|
||||
for (unsigned char i = 0; i < 44; i++) {
|
||||
if (readBit(i, ByteSet) == 0)
|
||||
{
|
||||
// Serial.print(0); //Debug only
|
||||
pinMode(data, OUTPUT);
|
||||
digitalWrite(data, 0);
|
||||
delay(delay_between_bits);
|
||||
}
|
||||
else
|
||||
{
|
||||
pinMode(data, INPUT); //We make the data pin high-impedance to let the pull-up of the Playstation motherboard make a 1
|
||||
// Serial.print(1); //Debug only
|
||||
pinMode(data, INPUT);
|
||||
delay(delay_between_bits);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pinMode(data, OUTPUT);
|
||||
digitalWrite(data, 0);
|
||||
delay(delay_between_injections);
|
||||
}
|
||||
|
||||
|
||||
void inject_SCEA()
|
||||
{
|
||||
//SCEE-array // Start Data Stop
|
||||
FLASH_ARRAY (boolean, SCEAData, 1,0,0,1,1,0,1,0,1,0,0,1,0,0,1,1,1,1,0,1,0,0,1,0,1,0,1,1,1,0,1,0,0,1,0,1,1,1,1,1,0,1,0,0); //SCEA: 1 00110101 00, 1 00111101 00, 1 01011101 00, 1 01111101 00
|
||||
|
||||
int bit_counter;
|
||||
|
||||
for (bit_counter = 0; bit_counter < 44; bit_counter = bit_counter + 1)
|
||||
{
|
||||
if (SCEAData[bit_counter] == 0)
|
||||
{
|
||||
static const unsigned char ByteSet[] PROGMEM = {0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11111010, 0b00000010};
|
||||
// Serial.println("inject_SCEA"); //Debug only
|
||||
for (unsigned char i = 0; i < 44; i++) {
|
||||
if (readBit(i, ByteSet) == 0)
|
||||
{
|
||||
// Serial.print(0); //Debug only
|
||||
pinMode(data, OUTPUT);
|
||||
digitalWrite(data, 0);
|
||||
delay(delay_between_bits);
|
||||
}
|
||||
else
|
||||
{
|
||||
pinMode(data, INPUT); //We make the data pin high-impedance to let the pull-up of the Playstation motherboard make a 1
|
||||
// Serial.print(1); //Debug only
|
||||
pinMode(data, INPUT);
|
||||
delay(delay_between_bits);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pinMode(data, OUTPUT);
|
||||
digitalWrite(data, 0);
|
||||
delay(delay_between_injections);
|
||||
}
|
||||
|
||||
|
||||
void inject_SCEI()
|
||||
{
|
||||
//SCEE-array // Start Data Stop
|
||||
FLASH_ARRAY (boolean, SCEIData, 1,0,0,1,1,0,1,0,1,0,0,1,0,0,1,1,1,1,0,1,0,0,1,0,1,0,1,1,1,0,1,0,0,1,0,1,1,0,1,1,0,1,0,0); //SCEI: 1 00110101 00, 1 00111101 00, 1 01011101 00, 1 01101101 00
|
||||
|
||||
int bit_counter;
|
||||
|
||||
for (bit_counter = 0; bit_counter < 44; bit_counter = bit_counter + 1)
|
||||
{
|
||||
if (SCEIData[bit_counter] == 0)
|
||||
{
|
||||
static const unsigned char ByteSet[] PROGMEM = {0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11011010, 0b00000010};
|
||||
// Serial.println("inject_SCEI"); //Debug only
|
||||
for (unsigned char i = 0; i < 44; i++) {
|
||||
if (readBit(i, ByteSet) == 0)
|
||||
{
|
||||
// Serial.print(0); //Debug only
|
||||
pinMode(data, OUTPUT);
|
||||
digitalWrite(data, 0);
|
||||
delay(delay_between_bits);
|
||||
}
|
||||
else
|
||||
{
|
||||
pinMode(data, INPUT); //We make the data pin high-impedance to let the pull-up of the Playstation motherboard make a 1
|
||||
// Serial.print(1); //Debug only
|
||||
pinMode(data, INPUT);
|
||||
delay(delay_between_bits);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
pinMode(data, OUTPUT);
|
||||
digitalWrite(data, 0);
|
||||
delay(delay_between_injections);
|
||||
|
||||
}
|
||||
|
||||
void inject_multiple_times(int number_of_injection_cycles)
|
||||
|
||||
|
||||
void inject_SCEx()
|
||||
{
|
||||
int cycle_counter;
|
||||
|
||||
for(cycle_counter = 0; cycle_counter < number_of_injection_cycles; cycle_counter = cycle_counter + 1)
|
||||
{
|
||||
inject_SCEE();
|
||||
inject_SCEA();
|
||||
inject_SCEI();
|
||||
}
|
||||
}
|
||||
|
||||
void inject_playstation()
|
||||
{
|
||||
//Variables
|
||||
int loop_counter;
|
||||
|
||||
//Code
|
||||
NTSC_fix();
|
||||
|
||||
delay(6900);
|
||||
digitalWrite(data, 0);
|
||||
pinMode(data, OUTPUT);
|
||||
unsigned char loop_counter;
|
||||
NTSC_fix(); //Only for SCPH-102 PAL. For other please, comment this function, and uncomment next delay.
|
||||
//delay(3000);
|
||||
delay(1200);
|
||||
pinMode(gate, OUTPUT);
|
||||
digitalWrite(gate, 0);
|
||||
delay(100);
|
||||
|
||||
pinMode(gate, OUTPUT);
|
||||
digitalWrite(gate, 0);
|
||||
|
||||
for (loop_counter = 0; loop_counter < 25; loop_counter = loop_counter + 1)
|
||||
{
|
||||
inject_SCEE();
|
||||
}
|
||||
|
||||
pinMode(gate, INPUT);
|
||||
pinMode(data, INPUT);
|
||||
delay(11000);
|
||||
|
||||
pinMode(gate, OUTPUT);
|
||||
digitalWrite(gate, 0);
|
||||
|
||||
for (loop_counter = 0; loop_counter < 60; loop_counter = loop_counter + 1)
|
||||
{
|
||||
inject_SCEE();
|
||||
}
|
||||
|
||||
|
||||
for (loop_counter = 0; loop_counter < 40; loop_counter = loop_counter + 1)
|
||||
if (digitalRead(lid) == HIGH)
|
||||
{
|
||||
DDRB = 0x00;
|
||||
void loop();
|
||||
}
|
||||
else
|
||||
{
|
||||
inject_SCEE();
|
||||
inject_SCEA();
|
||||
inject_SCEI();
|
||||
}
|
||||
|
||||
pinMode(gate, INPUT);
|
||||
pinMode(data, INPUT);
|
||||
injectFlag = 1;
|
||||
// Serial.println("inject_DONE!"); //Debug only
|
||||
|
||||
}
|
||||
|
||||
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
//--------------------------------------------------
|
||||
// Setup function - execution starts here!
|
||||
//--------------------------------------------------
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
inject_playstation();
|
||||
// Serial.begin(9600); //Debug only
|
||||
pinMode(lid, INPUT);
|
||||
pinMode(biosA18, INPUT);
|
||||
pinMode(data, INPUT);
|
||||
pinMode(gate, INPUT);
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Loop function - executes after the initial injection cycle
|
||||
//----------------------------------------------------------------
|
||||
|
||||
void loop()
|
||||
{
|
||||
if(lid == 0)
|
||||
if (digitalRead(lid) == HIGH && injectFlag == 1)
|
||||
{
|
||||
while(lid != 1); //Wait until the lid is closed again (after being opened) to initiate a new injection cycle
|
||||
inject_playstation();
|
||||
// Serial.println("Wait new cycle"); //Debug only
|
||||
injectFlag = 0;
|
||||
}
|
||||
|
||||
if (digitalRead(lid) == LOW && injectFlag == 0)
|
||||
{
|
||||
// Serial.println("Start Inject"); //Debug only
|
||||
inject_SCEx();
|
||||
}
|
||||
// if (digitalRead(lid) == HIGH && injectFlag == 0)
|
||||
// {
|
||||
// Serial.println("Opened before injecting!"); //Debug only
|
||||
// }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user