From 1074336cdb874df0425f9d07e453b6db6fa1d229 Mon Sep 17 00:00:00 2001 From: kalymos Date: Fri, 26 Dec 2025 20:03:28 +0100 Subject: [PATCH] Breaking down the main function into smaller modules. Modular breakdown: Separated main logic into - captureSubQ, - logic_SCPH_5903, - logic_Standard, - performInjectionSequence. Added support for SCPH-5903 by RepairBox Co-Authored-By: RepairBox <33960601+danielfergisz@users.noreply.github.com> --- PSNee/PSNee.ino | 288 ++++++++++++++++++++++++++++++----------------- PSNee/settings.h | 8 +- 2 files changed, 192 insertions(+), 104 deletions(-) diff --git a/PSNee/PSNee.ino b/PSNee/PSNee.ino index 7ecc82d..64d1f02 100644 --- a/PSNee/PSNee.ino +++ b/PSNee/PSNee.ino @@ -22,7 +22,7 @@ //#define SCPH_xxx1 // NTSC U/C | America. //#define SCPH_xxx2 // PAL | Europ. //#define SCPH_xxx3 // NTSC J | Asia. - +//#define SCPH_5903 // NTSC J | Asia VCD: // Models that require a BIOS patch. @@ -44,10 +44,10 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX SCPH model number // Data pin | 32-pin BIOS | 40-pin BIOS | BIOS version -------------------------------------------------------------------------------------------------*/ //#define SCPH_102 // DX - D0 | AX - A7 | | 4.4e - CRC 0BAD7EA9, 4.5e -CRC 76B880E5 -//#define SCPH_100 // DX - D0 | AX - A7 | | 4.3j - CRC F2AF798B +#define SCPH_100 // DX - D0 | AX - A7 | | 4.3j - CRC F2AF798B //#define SCPH_7500_9000 // DX - D0 | AX - A7 | | 4.0j - CRC EC541CD0 //#define SCPH_7000 // DX - D0 | AX - A7 | | 4.0j - CRC EC541CD0 Enables hardware support for disabling BIOS patching. -#define SCPH_5500 // DX - D0 | AX - A5 | | 3.0j - CRC FF3EEB8C +//#define SCPH_5500 // DX - D0 | AX - A5 | | 3.0j - CRC FF3EEB8C //#define SCPH_3500_5000 // DX - D0 | AX - A5 | AX - A4 | 2.2j - CRC 24FC7E17, 2.1j - CRC BC190209 //#define SCPH_3000 // DX - D5 | AX - A7, AY - A8 | AX - A6, AY - A7 | 1.1j - CRC 3539DEF6 //#define SCPH_1000 // DX - D5 | AX - A7, AY - A8 | AX - A6, AY - A7 | 1.0j - CRC 3B601FC8 @@ -128,6 +128,21 @@ volatile bool wfck_mode = 0; volatile bool Flag_Switch = 0; + +// Définition du type pour le pointeur de fonction +typedef void (*ConsoleLogicPtr)(uint8_t, uint8_t*); +////ConsoleLogicPtr currentLogic = logic_Standard; // Default + +// Variables de contrôle globales +volatile ConsoleLogicPtr currentLogic; +uint8_t scbuf[12] = { 0 }; +uint8_t hysteresis = 0; +uint16_t timeout_clock_counter = 0; + //Initializing values ​​for region code injection timing +#define DELAY_BETWEEN_BITS 4000 // 250 bits/s (microseconds) (ATtiny 8Mhz works from 3950 to 4100) PU-23 PU-22 MAX 4250 MIN 3850 +#define DELAY_BETWEEN_INJECTIONS 90 // The sweet spot is around 80~100. For all observed models, the worst minimum time seen is 72, and it works well up to 250. + + /*------------------------------------------------------------------------------------------------ Code section ------------------------------------------------------------------------------------------------*/ @@ -185,6 +200,157 @@ void board_detection(){ } } + + + + + +//****************************************************************************************************************** +// Reads a complete 12-byte SUBQ transmission from the CD drive. +// Uses clock-edge synchronization and includes a safety timeout for malformatted streams. +//****************************************************************************************************************** +void captureSubQ(void) { + //uint8_t bitpos = 0; + uint8_t scpos = 0; + uint8_t bitbuf = 0; + + GLOBAL_INTERRUPT_DISABLE; // Start critical section + + do { + bitbuf = 0; + for (uint8_t i = 0; i < 8; i++) { + while (PIN_SQCK_READ != 0); // Wait for clock LOWE + while (PIN_SQCK_READ == 0); // Wait for clock HIGH + + bitbuf >>= 1; // Décale d'abord + if (PIN_SUBQ_READ) bitbuf |= 0x80; // Insère le bit par le haut + } + scbuf[scpos++] = bitbuf; + } while (scpos < 12); + + // do { + // for (bitpos = 0; bitpos < 8; bitpos++) { + // while (PIN_SQCK_READ != 0) { // Wait for clock LOW + // timeout_clock_counter++; + // if (timeout_clock_counter > 1000) { + // scpos = 0; timeout_clock_counter = 0; bitbuf = 0; bitpos = 0; + // continue; + // } + // } + // while (PIN_SQCK_READ == 0); // Wait for clock HIGH + + // if (PIN_SUBQ_READ) { + // bitbuf |= (1 << bitpos); + // } + // timeout_clock_counter = 0; + // } + // scbuf[scpos] = bitbuf; + // scpos++; + // bitbuf = 0; + // } while (scpos < 12); + + GLOBAL_INTERRUPT_ENABLE; // End critical section +} + + + +/************************************************************************************** + * Processes sector data for the SCPH-5903 (Dual-interface PS1) to differentiate + * between PlayStation games and Video CDs (VCD). + * + * This heuristic uses an 'hysteresis' counter to stabilize disc detection: + * - Increases when a PSX Lead-In or valid game sector is identified. + * - Remains neutral/ignores VCD-specific Lead-In patterns. + * - Decreases (fades out) when the data does not match known patterns. + * + * isDataSector Boolean flag indicating if the current sector contains data. + +**************************************************************************************/ +void logic_SCPH_5903(uint8_t isDataSector) { + // Identify VCD Lead-In: Specific SCBUF patterns (0xA0/A1/A2) with sub-mode 0x02 + bool isVcdLeadIn = isDataSector && scbuf[1] == 0x00 && scbuf[6] == 0x00 && + (scbuf[2] == 0xA0 || scbuf[2] == 0xA1 || scbuf[2] == 0xA2) && + (scbuf[3] == 0x02); + + // Identify PSX Lead-In: Same SCBUF patterns but different sub-mode (!= 0x02) + bool isPsxLeadIn = isDataSector && scbuf[1] == 0x00 && scbuf[6] == 0x00 && + (scbuf[2] == 0xA0 || scbuf[2] == 0xA1 || scbuf[2] == 0xA2) && + (scbuf[3] != 0x02); + + if (isPsxLeadIn) { + hysteresis++; + } + else if (hysteresis > 0 && !isVcdLeadIn && + ((scbuf[0] == 0x01 || isDataSector) && scbuf[1] == 0x00 && scbuf[6] == 0x00)) { + hysteresis++; // Maintain/Increase confidence for valid non-VCD sectors + } + else if (hysteresis > 0) { + hysteresis--; // Patterns stop matching + } +} +/****************************************************************************************** + * Heuristic logic for standard PlayStation hardware (Non-VCD models). + * + * This function monitors disc sectors to identify genuine PlayStation discs: + * 1. Checks for specific Lead-In markers (Point A0, A1, A2 or Track 01). + * 2. Uses an incrementing 'hysteresis' counter to confirm disc validity. + * 3. Includes a 'fade-out' mechanism to reduce the counter if valid patterns are lost, + * effectively filtering out noise or read errors. + * + * isDataSector Boolean flag: true if the current sector is a data sector. + +******************************************************************************************/ + +void logic_Standard(uint8_t isDataSector) { + // Detect specific Lead-In patterns + if ((isDataSector && scbuf[1] == 0x00 && scbuf[6] == 0x00) && + (scbuf[2] == 0xA0 || scbuf[2] == 0xA1 || scbuf[2] == 0xA2 || + (scbuf[2] == 0x01 && (scbuf[3] >= 0x98 || scbuf[3] <= 0x02)))) { + hysteresis++; + } + // Maintain confidence if general valid sector markers are found + else if (hysteresis > 0 && + ((scbuf[0] == 0x01 || isDataSector) && scbuf[1] == 0x00 && scbuf[6] == 0x00)) { + hysteresis++; + } + else if (hysteresis > 0) { + hysteresis--; + } +} + + +//****************************************************************************************************************** +// Triggers the region code injection if the hysteresis threshold is reached. +//****************************************************************************************************************** +void performInjectionSequence() { + if (hysteresis >= HYSTERESIS_MAX) { + hysteresis = 11; // Reset to 11 for faster re-injection if head stays in TOC + +#ifdef LED_RUN + PIN_LED_ON; +#endif + PIN_DATA_OUTPUT; PIN_DATA_CLEAR; + if (!wfck_mode) { PIN_WFCK_OUTPUT; PIN_WFCK_CLEAR; } + + _delay_ms(DELAY_BETWEEN_INJECTIONS); + + for (uint8_t scex = 0; scex < 2; scex++) { + inject_SCEX(region[scex]); + } + + if (!wfck_mode) { PIN_WFCK_INPUT; } + PIN_DATA_INPUT; + +#ifdef LED_RUN + PIN_LED_OFF; +#endif + +#if defined(PSNEE_DEBUG_SERIAL_MONITOR) + Debug_Inject(); +#endif + } +} + /***************************************************************************************** Function: inject_SCEX @@ -231,9 +397,6 @@ void inject_SCEX(const char region) { // repetitive conditional checks inside the high-timing-sensitive loop. const uint8_t* ByteSet = (region == 'e') ? SCEEData : (region == 'a') ? SCEAData : SCEIData; - //Initializing values ​​for region code injection timing -#define DELAY_BETWEEN_BITS 4000 // 250 bits/s (microseconds) (ATtiny 8Mhz works from 3950 to 4100) PU-23 PU-22 MAX 4250 MIN 3850 -#define DELAY_BETWEEN_INJECTIONS 90 // The sweet spot is around 80~100. For all observed models, the worst minimum time seen is 72, and it works well up to 250. // for (uint8_t bit_counter = 0; bit_counter < 44; bit_counter++) { // // Check if the current bit is 0 @@ -303,6 +466,8 @@ void inject_SCEX(const char region) { void Init() { #if defined(ATmega328_168) || defined(ATmega32U4_16U4) || defined(ATtiny85_45_25) + + // Optimization test in progress... A; B; D; @@ -338,15 +503,6 @@ void Init() { } int main() { - uint8_t hysteresis = 0; - uint8_t scbuf[12] = { 0 }; // SUBQ bit storage - uint16_t timeout_clock_counter = 0; - uint8_t bitbuf = 0; - uint8_t bitpos = 0; - uint8_t scpos = 0; // scbuf position - // uint16_t lows = 0; - // uint8_t preset = 0; - // uint32_t totalSamples = 400000; Init(); @@ -372,50 +528,20 @@ int main() { Debug_Log(lows, wfck_mode); #endif + +#ifdef SCPH_5903 + currentLogic = logic_SCPH_5903; +#else + currentLogic = logic_Standard; +#endif + while (1) { _delay_ms(1); /* Start with a small delay, which can be necessary in cases where the MCU loops too quickly and picks up the laster SUBQ trailing end*/ - GLOBAL_INTERRUPT_DISABLE; // start critical section - - // Capture 8 bits for 12 runs > complete SUBQ transmission - do { - for (bitpos = 0; bitpos < 8; bitpos++) { - while (PIN_SQCK_READ != 0) // wait for clock to go low - { - timeout_clock_counter++; - // a timeout resets the 12 byte stream in case the PSX sends malformatted clock pulses, as happens on bootup - if (timeout_clock_counter > 1000) { - scpos = 0; - timeout_clock_counter = 0; - bitbuf = 0; - bitpos = 0; - continue; - } - } - - // Wait for clock to go high - while (PIN_SQCK_READ == 0); - - if (PIN_SUBQ_READ) // If clock pin high - { - bitbuf |= 1 << bitpos; // Set the bit at position bitpos in the bitbuf to 1. Using OR combined with a bit shift - } - - timeout_clock_counter = 0; // no problem with this bit - } - - scbuf[scpos] = bitbuf; // One byte done - scpos++; - bitbuf = 0; - } - - while (scpos < 12); // Repeat for all 12 bytes - - GLOBAL_INTERRUPT_ENABLE; // End critical section - + captureSubQ(); #if defined(PSNEE_DEBUG_SERIAL_MONITOR) @@ -433,62 +559,22 @@ int main() { //This variable initialization macro is to replace (0x41) with a filter that will check that only the three most significant bits are correct. 0x001xxxxx uint8_t isDataSector = (((scbuf[0] & 0x40) == 0x40) && (((scbuf[0] & 0x10) == 0) && ((scbuf[0] & 0x80) == 0))); + + + // 2. Execute selected logic through function pointer + currentLogic(isDataSector, scbuf); + - if ( - (isDataSector && scbuf[1] == 0x00 && scbuf[6] == 0x00) && // [0] = 41 means psx game disk. the other 2 checks are garbage protection - (scbuf[2] == 0xA0 || scbuf[2] == 0xA1 || scbuf[2] == 0xA2 || // if [2] = A0, A1, A2 .. - (scbuf[2] == 0x01 && (scbuf[3] >= 0x98 || scbuf[3] <= 0x02))) // .. or = 01 but then [3] is either > 98 or < 02 - ) { - hysteresis++; - } - // This CD has the wobble into CD-DA space. (started at 0x41, then went into 0x01) - else if (hysteresis > 0 && ((scbuf[0] == 0x01 || isDataSector) && (scbuf[1] == 0x00 /*|| scbuf[1] == 0x01*/) && scbuf[6] == 0x00)) { - hysteresis++; - } - // None of the above. Initial detection was noise. Decrease the counter. - else if (hysteresis > 0) { - hysteresis--; - } - // hysteresis value "optimized" using very worn but working drive on ATmega328 @ 16Mhz - // should be fine on other MCUs and speeds, as the PSX dictates SUBQ rate - if (hysteresis >= HYSTERESIS_MAX) { - // If the read head is still here after injection, resending should be quick. - // Hysteresis naturally goes to 0 otherwise (the read head moved). - hysteresis = 11; #ifdef LED_RUN PIN_LED_ON; #endif -/*------------------------------------------------------------------------------- - Executes the region code injection sequence. --------------------------------------------------------------------------------*/ - - PIN_DATA_OUTPUT; - PIN_DATA_CLEAR; - - if (!wfck_mode) // If wfck_mode is fals (oldmode) - { - PIN_WFCK_OUTPUT; - PIN_WFCK_CLEAR; - } - - _delay_ms(DELAY_BETWEEN_INJECTIONS); // HC-05 waits for a bit of silence (pin low) before it begins decoding. - - // inject symbols now. 2 x 3 runs seems optimal to cover all boards - for (uint8_t scex = 0; scex < 2; scex++) { - inject_SCEX(region[scex]); - } - - if (!wfck_mode) // Set WFCK pin input - { - PIN_WFCK_INPUT; - } - - PIN_DATA_INPUT; + // 3. Check hysteresis and inject if necessary + performInjectionSequence(); #ifdef LED_RUN PIN_LED_OFF; @@ -498,6 +584,6 @@ int main() { Debug_Inject(); #endif - } + //} } } diff --git a/PSNee/settings.h b/PSNee/settings.h index 181db7e..c98c77f 100644 --- a/PSNee/settings.h +++ b/PSNee/settings.h @@ -124,7 +124,9 @@ const char region[3] = {'a', 'a', 'a'}; const char region[3] = {'e', 'e', 'e'}; #endif -#if defined(SCPH_100) || defined(SCPH_7500_9000) || defined(SCPH_7000) || defined(SCPH_5500) || defined(SCPH_3500_5000) || defined(SCPH_3000) || defined(SCPH_1000) || defined(SCPH_xxx3) // NTSC J | Asia. +#if defined(SCPH_100) || defined(SCPH_7500_9000) || defined(SCPH_7000) || \ + defined(SCPH_5500) || defined(SCPH_3500_5000) || defined(SCPH_3000) || \ + defined(SCPH_1000) || defined(SCPH_xxx3) || defined(SCPH_5903) // NTSC J | Asia. const char region[3] = {'i', 'i', 'i'}; #endif @@ -192,13 +194,13 @@ void Debug_Inject(){ // Confirmation of region code injection #if !defined(SCPH_xxx3) && \ !defined(SCPH_102) && !defined(SCPH_101) && !defined(SCPH_100) && !defined(SCPH_7500_9000) && \ !defined(SCPH_7000) && !defined(SCPH_5500) && !defined(SCPH_3500_5000) && !defined(SCPH_3000) && \ - !defined(SCPH_1000) && \ + !defined(SCPH_1000) && !defined(SCPH_5903) &&\ !defined(SCPH_xxx1) && !defined(SCPH_xxx2) #error "Console not selected! Please uncoment #define with SCPH model number." #elif !defined(SCPH_xxx3) ^ \ defined(SCPH_102) ^ defined(SCPH_101) ^ defined(SCPH_100) ^ defined(SCPH_7500_9000) ^ \ defined(SCPH_7000) ^ defined(SCPH_5500) ^ defined(SCPH_3500_5000) ^ defined(SCPH_3000) ^ \ - defined(SCPH_1000) ^ defined(SCPH_xxxx) ^ \ + defined(SCPH_1000) ^ defined(SCPH_xxxx) ^ defined(SCPH_5903) ^ \ defined(SCPH_xxx1) ^ defined(SCPH_xxx2) #error "May be selected only one console! Please check #define with SCPH model number." #endif