diff --git a/PsNee.ino b/PsNee.ino index b5680c3..4445de0 100644 --- a/PsNee.ino +++ b/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 - -//-------------------------------------------------- -// 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 + // } }