From 0ff2612c7c9b89d058d73a732f582eaa2e1df5c6 Mon Sep 17 00:00:00 2001 From: kalymos Date: Sun, 1 Mar 2026 19:03:32 +0100 Subject: [PATCH] Refactor: Optimization of board detection, injection logic, and global nomenclature - Board Detection: Added 300ms stabilization delay to filter PU-7/20 power-up noise and sync with PU-22+ oscillating signals. - Injection: Added conditional break to allow single-region selection and updated WFCK modulation for 7.3/14.6 kHz compatibility. - Logic: Optimized sector filtering using bitmask (0xD0) and improved hysteresis tracking for Standard and SCPH-5903 models. - Code Quality: Standardized nomenclature (subqBuffer, hysteresis, pulseCounter) and added comprehensive English documentation. - Performance: Reduced binary size by optimizing loops and variable types. --- PSNee/BIOS_patching.h | 68 ++-- PSNee/PSNee.ino | 729 ++++++++++-------------------------------- PSNee/settings.h | 39 ++- 3 files changed, 234 insertions(+), 602 deletions(-) diff --git a/PSNee/BIOS_patching.h b/PSNee/BIOS_patching.h index 687082c..ff958b7 100644 --- a/PSNee/BIOS_patching.h +++ b/PSNee/BIOS_patching.h @@ -4,8 +4,8 @@ #ifdef BIOS_PATCH - volatile uint8_t pulse_counter = 0; - volatile uint8_t patch_done = 0; + volatile uint8_t pulseCounter = 0; + volatile uint8_t patchStep = 0; #ifdef INTERRUPT_RISING @@ -20,9 +20,9 @@ */ - //if (--pulse_counter == 0) { - pulse_counter++; - if (pulse_counter == PULSE_COUNT){ // If pulse_counter reaches the value defined by PULSE_COUNT + //if (--pulseCounter == 0) { + pulseCounter++; + if (pulseCounter == PULSE_COUNT){ // If pulseCounter reaches the value defined by PULSE_COUNT /* * PHASE 4: Precision Bit Alignment * Once the PULSE_COUNT is reached, a micro-delay (BIT_OFFSET) is applied. @@ -40,8 +40,8 @@ _delay_us (OVERRIDE); PIN_DX_INPUT; PIN_AX_INTERRUPT_DISABLE; - pulse_counter = 0; - patch_done = 1; // patch_done is set to 1, indicating that the first patch is completed. + pulseCounter = 0; + patchStep = 1; // patchStep is set to 1, indicating that the first patch is completed. } } @@ -72,10 +72,10 @@ */ _delay_ms(BOOT_OFFSET); // Armed for hardware detection - //pulse_counter = PULSE_COUNT; + //pulseCounter = PULSE_COUNT; PIN_AX_INTERRUPT_RISING; PIN_AX_INTERRUPT_ENABLE; - while (patch_done != 1); // Wait for the first stage of the patch to complete: + while (patchStep != 1); // Wait for the first stage of the patch to complete: } #endif @@ -83,8 +83,8 @@ ISR(PIN_AX_INTERRUPT_VECTOR) { - pulse_counter++; - if (pulse_counter == PULSE_COUNT){ + pulseCounter++; + if (pulseCounter == PULSE_COUNT){ _delay_us (BIT_OFFSET); PIN_DX_OUTPUT; _delay_us (OVERRIDE); @@ -92,8 +92,8 @@ PIN_AX_INTERRUPT_DISABLE; - pulse_counter = 0; - patch_done = 1; + pulseCounter = 0; + patchStep = 1; } } @@ -112,14 +112,14 @@ PIN_AX_INTERRUPT_FALLING; PIN_AX_INTERRUPT_ENABLE; - while (patch_done != 1); + while (patchStep != 1); } #endif #ifdef INTERRUPT_RISING_HIGH_PATCH ISR(PIN_AX_INTERRUPT_VECTOR) { - pulse_counter++; - if (pulse_counter == PULSE_COUNT){ + pulseCounter++; + if (pulseCounter == PULSE_COUNT){ _delay_us (BIT_OFFSET); PIN_DX_SET; PIN_DX_OUTPUT; @@ -127,22 +127,22 @@ PIN_DX_CLEAR; PIN_DX_INPUT; PIN_AX_INTERRUPT_DISABLE; - pulse_counter = 0; - patch_done = 1; + pulseCounter = 0; + patchStep = 1; } } ISR(PIN_AY_INTERRUPT_VECTOR){ - pulse_counter++; + pulseCounter++; - if (pulse_counter == PULSE_COUNT_2) { + if (pulseCounter == PULSE_COUNT_2) { _delay_us (BIT_OFFSET_2); PIN_DX_OUTPUT; _delay_us (OVERRIDE_2); PIN_DX_INPUT; PIN_AY_INTERRUPT_DISABLE; - patch_done = 2; + patchStep = 2; } } @@ -161,7 +161,7 @@ PIN_AX_INTERRUPT_ENABLE; - while (patch_done != 1); + while (patchStep != 1); while (PIN_AY_READ != 0); @@ -169,7 +169,7 @@ PIN_AY_INTERRUPT_RISING; PIN_AY_INTERRUPT_ENABLE; - while (patch_done != 2); + while (patchStep != 2); } #endif @@ -202,8 +202,8 @@ #ifdef BIOS_PATCH_2 - volatile uint8_t pulse_counter = 0; - volatile uint8_t patch_done = 0; + volatile uint8_t pulseCounter = 0; + volatile uint8_t patchStep = 0; // --- MAIN INTERRUPT SERVICE ROUTINE (ADDRESS AX) --- ISR(PIN_AX_INTERRUPT_VECTOR) { @@ -211,7 +211,7 @@ * PHASE 3: Pulse Counting (Inside ISR) * Decrementing towards zero is the fastest operation on AVR architecture. */ - if (--pulse_counter == 0) { + if (--pulseCounter == 0) { /* PHASE 4: Precision Bit Alignment */ _delay_us(BIT_OFFSET); @@ -230,20 +230,20 @@ PIN_DX_INPUT; // Immediately release the Data Bus PIN_AX_INTERRUPT_DISABLE; - patch_done = 1; // Notify Stage 1 completion + patchStep = 1; // Notify Stage 1 completion } } // --- SECONDARY INTERRUPT SERVICE ROUTINE (ADDRESS AY - Variant 3) --- #ifdef INTERRUPT_RISING_HIGH_PATCH ISR(PIN_AY_INTERRUPT_VECTOR) { - if (--pulse_counter == 0) { + if (--pulseCounter == 0) { _delay_us(BIT_OFFSET_2); PIN_DX_OUTPUT; _delay_us(OVERRIDE_2); PIN_DX_INPUT; PIN_AY_INTERRUPT_DISABLE; - patch_done = 2; // Notify Stage 2 completion + patchStep = 2; // Notify Stage 2 completion } } #endif @@ -265,8 +265,8 @@ _delay_ms(BOOT_OFFSET); // Countdown Preparation (Optimized for speed) - pulse_counter = PULSE_COUNT; - patch_done = 0; + pulseCounter = PULSE_COUNT; + patchStep = 0; // Dynamic Interrupt Configuration #if defined(INTERRUPT_RISING) || defined(INTERRUPT_RISING_HIGH_PATCH) @@ -277,7 +277,7 @@ // Arm Hardware Interrupt PIN_AX_INTERRUPT_ENABLE; - while (patch_done != 1); // Block until first patch is applied + while (patchStep != 1); // Block until first patch is applied /* * OPTIONAL PHASE: Secondary Patch (Variant 3 only) @@ -287,10 +287,10 @@ while (PIN_AY_READ != 0); // Ensure AY is low before arming _delay_ms(FOLLOWUP_OFFSET); - pulse_counter = PULSE_COUNT_2; // Re-load counter for AY pulses + pulseCounter = PULSE_COUNT_2; // Re-load counter for AY pulses PIN_AY_INTERRUPT_RISING; PIN_AY_INTERRUPT_ENABLE; - while (patch_done != 2); // Block until second patch is applied + while (patchStep != 2); // Block until second patch is applied #endif } diff --git a/PSNee/PSNee.ino b/PSNee/PSNee.ino index 56715d8..ab779f8 100644 --- a/PSNee/PSNee.ino +++ b/PSNee/PSNee.ino @@ -20,8 +20,8 @@ SCPH model number // region code | region -------------------------------------------------------------------------------------------------*/ //#define SCPH_xxxx // | Universal. -//#define SCPH_xxx1 // NTSC U/C | America. -#define SCPH_xxx2 // PAL | Europ. +#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. @@ -139,11 +139,15 @@ volatile uint8_t wfck_mode = 0; typedef void (*ConsoleLogicPtr)(uint8_t isDataSector); #ifdef SCPH_5903 -void logic_SCPH_5903(uint8_t isDataSector); -volatile ConsoleLogicPtr currentLogic = logic_SCPH_5903; + // Forward declaration + void logic_SCPH_5903(uint8_t isDataSector); + // Pointer assignment + volatile ConsoleLogicPtr currentLogic = logic_SCPH_5903; #else -void logic_Standard(uint8_t isDataSector); -volatile ConsoleLogicPtr currentLogic = logic_Standard; + // Forward declaration + void logic_Standard(uint8_t isDataSector); + // Pointer assignment + volatile ConsoleLogicPtr currentLogic = logic_Standard; #endif @@ -152,7 +156,8 @@ volatile ConsoleLogicPtr currentLogic = logic_Standard; //volatile ConsoleLogicPtr currentLogic = logic_Standard; // Variables de contrôle globales -uint8_t scbuf[12] = { 0 }; +// Global buffer to store the 12-byte Sub-Q channel data +uint8_t subqBuffer[12]; uint8_t hysteresis = 0; //Initializing values ​​for region code injection timing @@ -185,174 +190,89 @@ uint8_t hysteresis = 0; -----------------------------------------------------------------------*/ -// void board_detection() { -// uint16_t pulses = 0; -// uint32_t totalSamples = 600000; // Timeout/Sampling window to limit detection duration - -// // Runs until 600,000 cycles pass -// while (totalSamples--) { -// // If the current pin state is HIGH, wait for it to go LOW -// if (PIN_WFCK_READ) { -// // Wait for falling edge OR timeout to prevent infinite hang -// while (PIN_WFCK_READ && --totalSamples); - -// pulses++; - -// // High count (> 500) oscillating signal (Newer boards) -// if (pulses > 500) { -// wfck_mode = 1; // Target: PU-22 or newer -// return; -// } -// } -// } - -// // Low count implies a static signal (Older boards) -// wfck_mode = 0; // Target: PU-7 to PU-20 - -// #if defined(PSNEE_DEBUG_SERIAL_MONITOR) -// Debug_Log(wfck_mode); -// #endif -// } - -// void board_detection() { -// // Universal 16-bit timeout (covers ~100ms at 16MHz) -// uint16_t timeout = 60000; -// uint8_t pulses = 200; - -// while (--timeout) { -// // 1. Wait for RISING edge (Signal goes HIGH) -// if (PIN_WFCK_READ) { -// // 2. Wait for FALLING edge (Signal goes LOW) -// while (PIN_WFCK_READ && --timeout); - -// // If we find 500 falling edges, it's a newer board (WFCK) -// if (--pulses == 0) { -// wfck_mode = 1; // PU-22+ -// return; -// } -// } -// } -// // Timeout reached or static signal (Gate) -// wfck_mode = 0; // PU-7 to PU-20 -// } - -// void board_detection() { -// uint16_t timeout = 60000; -// uint8_t pulses = 100; - -// // 1. SYNC PHASE: Bypasses the initial "Blank" state (High or Low) -// uint8_t start_state = PIN_WFCK_READ; -// while (PIN_WFCK_READ == start_state && --timeout); - -// // 2. DETECTION PHASE: Counts 100 Falling Edges -// while (--timeout) { -// if (PIN_WFCK_READ) { -// // Wait for falling edge -// while (PIN_WFCK_READ && --timeout); - -// if (--pulses == 0) { -// wfck_mode = 1; // Oscillating WFCK (PU-22+) -// return; -// } -// } -// } - -// wfck_mode = 0; // Static Signal or Timeout (PU-7 to PU-20) -// } void board_detection() { - uint16_t timeout = 60000; - uint8_t pulses = 100; - uint8_t mode = 0; + // Default to static signal (PU-7 to PU-20) + wfck_mode = 0; - // 1. SYNC PHASE: Wait for the first transition to align with the signal - uint8_t start_state = PIN_WFCK_READ; - while (PIN_WFCK_READ == start_state && --timeout); + /* + INITIAL STABILIZATION DELAY (300ms) + PU-7 to PU-20: Voltage climbs slowly (up to 54ms) then stays HIGH (static). + PU-22+: Signal only starts oscillating (~7.3kHz) after approximately 297ms. + Waiting 300ms bypasses all power-up transients and initial noise. + */ + _delay_ms(300); - // 2. DETECTION PHASE: Count edges with minimal logic inside the loop - while (timeout > 0) { - // Wait for WFCK to go HIGH - while (!PIN_WFCK_READ && --timeout); - if (timeout == 0) break; + // Sampling window to detect the oscillating signal + uint16_t detectionWindow = 10000; + + while (--detectionWindow) { + /* + On older boards (PU-7/20), the signal is now a solid HIGH. + If we detect a LOW state, it's a potential oscillation from a newer board. + */ + if (!PIN_WFCK_READ) { + // Small debounce delay to filter out micro-glitches or remaining noise + uint8_t debounce = 100; + while (--debounce); - // Wait for WFCK to go LOW (Falling Edge) - while (PIN_WFCK_READ && --timeout); - if (timeout == 0) break; - - // Count pulses - if (--pulses == 0) { - mode = 1; // Oscillating signal detected - break; - } + /* + VERIFICATION: If the signal is STILL low, it confirms a real + clock cycle (WFCK). Older boards will never reach this state + once stabilized at HIGH. + */ + if (!PIN_WFCK_READ) { + wfck_mode = 1; // Target: PU-22 or newer + return; + } } + } - wfck_mode = mode; +#if defined(PSNEE_DEBUG_SERIAL_MONITOR) + Debug_Log(wfck_mode); +#endif } + + + //****************************************************************************************************************** // 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 scpos = 0; + // Total bytes to read from the CD-ROM subcode channel + uint8_t bytesRemaining = 12; + uint8_t* bufferPtr = subqBuffer; do { - uint8_t bitbuf = 0; - // Process 8 bits for each of the 12 bytes - for (uint8_t i = 0; i < 8; i++) { - // Wait for SQCK Clock: sampling occurs on the Rising Edge - while (PIN_SQCK_READ); // Wait for LOW - while (!PIN_SQCK_READ); // Wait for HIGH + uint8_t currentByte = 0; + uint8_t bitsToRead = 8; + + while (bitsToRead--) { + // PHASE 1: Wait for Clock (SQCK) to go LOW then HIGH (Sampling on Rising Edge) + // This ensures data is stable on the SUBQ pin before reading. + while (PIN_SQCK_READ); // Wait for falling edge + while (!PIN_SQCK_READ); // Wait for rising edge - // Shift and sample SUBQ data pin - bitbuf >>= 1; + // PHASE 2: Shift bit into the byte (LSB first) + currentByte >>= 1; if (PIN_SUBQ_READ) { - bitbuf |= 0x80; + currentByte |= 0x80; } } - // Direct index storage for smaller binary size - scbuf[scpos++] = bitbuf; - } while (scpos < 12); + // Store reconstructed byte and advance pointer + *bufferPtr++ = currentByte; + } while (--bytesRemaining); // Faster than (bytesRemaining < 12) #if defined(PSNEE_DEBUG_SERIAL_MONITOR) - Debug_Scbuf(scbuf); + logSubQ(subqBuffer); #endif } - -// void captureSubQ(void) { -// uint8_t scpos = 12; // Start with total count for decrement-to-zero optimization -// uint8_t* ptr = scbuf; - -// do { -// uint8_t bitbuf = 0; -// uint8_t i = 8; - -// while (i--) { -// // PHASE 1: Wait for Clock (SQCK) to go LOW then HIGH (Sampling on Rising Edge) -// while (PIN_SQCK_READ); // Wait for LOW -// while (!PIN_SQCK_READ); // Wait for HIGH - -// // PHASE 2: Shift buffer and sample the SUBQ pin -// // Optimized bit rotation -// bitbuf >>= 1; -// if (PIN_SUBQ_READ) { -// bitbuf |= 0x80; -// } -// } -// // Store byte and move pointer -// *ptr++ = bitbuf; -// } while (--scpos); // Decrement-to-zero is faster than (scpos < 12) - -// #if defined(PSNEE_DEBUG_SERIAL_MONITOR) -// Debug_Scbuf(scbuf); -// #endif -// } - - - /************************************************************************************** * Processes sector data for the SCPH-5903 (Dual-interface PS1) to differentiate * between PlayStation games and Video CDs (VCD). @@ -366,62 +286,35 @@ void captureSubQ(void) { **************************************************************************************/ -// void logic_SCPH_5903(uint8_t isDataSector) { -// // Optimization: Pre-check common markers (1 and 6) to save CPU cycles -// if (scbuf[1] == 0x00 && scbuf[6] == 0x00) { - -// // Identify Lead-In patterns (0xA0, 0xA1, 0xA2) -// if (isDataSector && (scbuf[2] >= 0xA0 && scbuf[2] <= 0xA2)) { -// // Identify PSX Lead-In: same patterns but sub-mode is NOT 0x02 -// if (scbuf[3] != 0x02) { -// hysteresis++; -// return; -// } -// // If it is 0x02, it's a VCD Lead-In: we fall through to the final else if -// } -// // Maintain/Increase confidence for valid non-VCD sectors (0x01 or Data) -// else if (hysteresis > 0 && (scbuf[0] == 0x01 || isDataSector)) { -// hysteresis++; -// return; -// } -// } +void processScph5903Logic(uint8_t isDataSector) { + uint8_t currentHysteresis = hysteresis; -// // Patterns stop matching or VCD Lead-In detected: decrease confidence -// if (hysteresis > 0) { -// hysteresis--; -// } -// } + // Fast filtering: most sectors fail here by checking sync markers (index 1 and 6) + if (subqBuffer[1] == 0x00 && subqBuffer[6] == 0x00) { + uint8_t pointAddress = subqBuffer[2]; - -void logic_SCPH_5903(uint8_t isDataSector) { - uint8_t h = hysteresis; - - // Fast check: most sectors will fail here (sync markers 1 and 6) - if (scbuf[1] == 0x00 && scbuf[6] == 0x00) { - uint8_t s2 = scbuf[2]; - - // Combine all "Success" conditions for a single increment path: - // 1. Valid PSX Lead-In: isDataSector AND (A0-A2 range) AND NOT VCD (sub-mode 0x02) - // 2. OR General valid sector: (Mode 0x01 or Data) while already tracking (h > 0) - if ( (isDataSector && (uint8_t)(s2 - 0xA0) <= 2 && scbuf[3] != 0x02) || - (h > 0 && (scbuf[0] == 0x01 || isDataSector)) ) + /* + INCREMENT CONDITIONS: + 1. Valid PSX Lead-In: data sector AND Point A0-A2 range AND NOT VCD (sub-mode 0x02). + 2. Tracking Maintenance: Valid sector (Mode 0x01 or Data) while already synchronized. + */ + if ( (isDataSector && (uint8_t)(pointAddress - 0xA0) <= 2 && subqBuffer[3] != 0x02) || + (currentHysteresis > 0 && (subqBuffer[0] == 0x01 || isDataSector)) ) { - hysteresis = h + 1; + hysteresis = currentHysteresis + 1; return; } } - // Default: no match found or VCD Lead-In detected, decrease confidence - if (h > 0) { - hysteresis = h - 1; + /* + DECREMENT CONDITION: + No match found or VCD Lead-In detected. Slowly decrease confidence level. + */ + if (currentHysteresis > 0) { + hysteresis = currentHysteresis - 1; } } - - - - - /****************************************************************************************** * Heuristic logic for standard PlayStation hardware (Non-VCD models). * @@ -435,367 +328,80 @@ void logic_SCPH_5903(uint8_t isDataSector) { ******************************************************************************************/ -// void logic_Standard(uint8_t isDataSector) { -// // Optimization: Check common markers once (scbuf[1] and scbuf[6]) -// if (scbuf[1] == 0x00 && scbuf[6] == 0x00) { +void processStandardLogic(uint8_t isDataSector) { + uint8_t currentHysteresis = hysteresis; + + // Fast filtering: most sectors fail here by checking sync markers (index 1 and 6) + if (subqBuffer[1] == 0x00 && subqBuffer[6] == 0x00) { + uint8_t pointAddress = subqBuffer[2]; -// // Detect specific Lead-In patterns (A0, A1, A2 or 01 with specific time ranges) -// if (isDataSector && (scbuf[2] >= 0xA0 || -// (scbuf[2] == 0x01 && (scbuf[3] >= 0x98 || scbuf[3] <= 0x02)))) { -// hysteresis++; -// return; // Pattern found, exit early -// } - -// // Maintain confidence if general valid sector markers are found -// if (hysteresis > 0 && (scbuf[0] == 0x01 || isDataSector)) { -// hysteresis++; -// return; -// } -// } - -// // No valid pattern found: decrease confidence -// if (hysteresis > 0) { -// hysteresis--; -// } -// } - -// void logic_Standard(uint8_t isDataSector) { -// uint8_t h = hysteresis; - -// // Combine scbuf check and Lead-In detection into a single path -// if (scbuf == 0 && scbuf == 0) { -// uint8_t s2 = scbuf; -// // Logical OR for all "Increment" conditions -// if ( (isDataSector && (s2 >= 0xA0 || (s2 == 0x01 && (scbuf >= 0x98 || scbuf <= 0x02)))) || -// (h > 0 && (scbuf == 0x01 || isDataSector)) ) -// { -// hysteresis = h + 1; -// return; -// } -// } - -// // Single point for decrement -// if (h > 0) hysteresis = h - 1; -// } - - -void logic_Standard(uint8_t isDataSector) { - uint8_t h = hysteresis; - - // Fast reject: most sectors fail here - if (scbuf[1] == 0x00 && scbuf[6] == 0x00) { - uint8_t s2 = scbuf[2]; - - // Single unified condition for "Increment" - // 1. Lead-In: isDataSector AND (s2>=A0 OR (s2==01 AND (s3>=98 OR s3<=02))) - // 2. Standard: (h>0) AND (s0==01 OR isDataSector) - if ( (isDataSector && (s2 >= 0xA0 || (s2 == 0x01 && ( (uint8_t)(scbuf[3] - 0x03) >= 0xF5)))) || - (h > 0 && (scbuf[0] == 0x01 || isDataSector)) ) + /* + INCREMENT CONDITIONS: + 1. Lead-In Detection: + - Point A0-A2 or higher (TOC info). + - OR Point 01 with a timestamp near the spiral start: + Checking if index 3 is >= 98 or <= 02 using unsigned wrap-around: + (uint8_t)(subqBuffer[3] - 0x03) >= 0xF5 (245) covers 0x98 to 0x02. + 2. Tracking Maintenance: + - Valid sector (Mode 0x01 or Data) while already synchronized. + */ + if ( (isDataSector && (pointAddress >= 0xA0 || (pointAddress == 0x01 && ( (uint8_t)(subqBuffer[3] - 0x03) >= 0xF5)))) || + (currentHysteresis > 0 && (subqBuffer[0] == 0x01 || isDataSector)) ) { - hysteresis = h + 1; + hysteresis = currentHysteresis + 1; return; } } - // Default: decrement - if (h > 0) hysteresis = h - 1; + /* + DECREMENT CONDITION: + No valid match found. Slowly decrease confidence level. + */ + if (currentHysteresis > 0) { + hysteresis = currentHysteresis - 1; + } } - - - /********************************************************************************************* - * Executes the SCEx signal injection sequence to bypass regional lockout. + * Executes the SCEx injection sequence to bypass the CD-ROM regional lockout. * - * This function handles the precise timing required to send the 44-bit security - * strings. It supports both legacy "Logic Gate" mode and modern "WFCK Modulation" - * for compatibility across all console revisions (PU-7 to PM-41). + * This function supports two hardware-specific injection methods: + * 1. Legacy Gate Mode (PU-7 to PU-20): Modchip acts as a logic gate to pull + * the signal down. WFCK is simulated by the chip if necessary. + * 2. WFCK Modulation (PU-22+): Modchip modulates the DATA signal in + * sync with the console's real WFCK clock. * - * injectSCEx 0:NTSC-J SCEI, 1:NTSC-U/C SCEA, 2:PAL SCEE, 3:Universal (Cycles through all). + * NOTE: WFCK frequency is approx. 7.3 kHz during initialization/region check, + * but doubles to 14.6 kHz during normal data reading. The modulation loop + * handles both speeds as it syncs directly to the signal edges. *********************************************************************************************/ -// void performInjectionSequence(uint8_t injectSCEx) { -// // 44-bit SCEx strings for Japan Asia, USA, and Europe -// static const uint8_t allRegionsSCEx[3][6] = { -// { 0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11011010, 0b00000010 }, // SCEI -// { 0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11111010, 0b00000010 }, // SCEA -// { 0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11101010, 0b00000010 } // SCEE -// }; - -// hysteresis = 11; -// #ifdef LED_RUN -// PIN_LED_ON; -// #endif - -// // Pin initialization -// PIN_DATA_OUTPUT; -// PIN_DATA_CLEAR; - -// if (!wfck_mode) { -// PIN_WFCK_OUTPUT; PIN_WFCK_CLEAR; -// } - -// _delay_ms(DELAY_BETWEEN_INJECTIONS); - -// // Injection loop (3 cycles) -// for (uint8_t i = 0; i < 3; i++) { - -// // Mode 3: cycles through all regions; Others: stay on fixed region -// uint8_t regionIndex = (injectSCEx == 3) ? i : injectSCEx; -// const uint8_t* ByteSet = allRegionsSCEx[regionIndex]; - -// // Process 6 bytes to reach 44 bits -// for (uint8_t b = 0; b < 6; b++) { -// uint8_t currentByte = ByteSet[b]; - -// for (uint8_t bit = 0; bit < 8; bit++) { -// // Stop exactly at 44 bits (6th byte, 4th bit) -// if (b == 5 && bit == 4) break; - -// // Bit-level extraction: isolate the target bit and prepare next -// uint8_t currentBit = currentByte & 0x01; -// currentByte >>= 1; - -// if (!wfck_mode) { -// // LOGIC GATE MODE (Old boards) -// if (currentBit == 0) { -// PIN_DATA_OUTPUT; PIN_DATA_CLEAR; -// } -// else { -// PIN_DATA_INPUT; // High Impedance = 1 -// } -// _delay_us(DELAY_BETWEEN_BITS); -// } -// else { -// // WFCK MODULATION (Newer boards / PU-18+) -// if (currentBit == 0) { -// PIN_DATA_CLEAR; -// _delay_us(DELAY_BETWEEN_BITS); -// } else { -// // Synchronize injection with WFCK clock edges -// uint8_t count = 30; -// while (count--) { -// while (PIN_WFCK_READ); // Wait for LOW -// PIN_DATA_CLEAR; -// while (!PIN_WFCK_READ); // Wait for HIGH -// PIN_DATA_SET; -// } -// } -// } -// } -// } -// // Inter-string silence -// PIN_DATA_OUTPUT; -// PIN_DATA_CLEAR; -// _delay_ms(DELAY_BETWEEN_INJECTIONS); -// } - -// // Cleanup: Set pins to High-Z (Safe mode) -// 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 -// } - -// void performInjectionSequence(uint8_t injectSCEx) { -// // 44-bit SCEx patterns (6 bytes: 5 full + 4 bits) -// static const uint8_t allRegionsSCEx[3][6] = { -// { 0x59, 0xC9, 0x4B, 0x5D, 0xDA, 0x02 }, // SCEI -// { 0x59, 0xC9, 0x4B, 0x5D, 0xFA, 0x02 }, // SCEA -// { 0x59, 0xC9, 0x4B, 0x5D, 0xEA, 0x02 } // SCEE -// }; - -// hysteresis = 11; -// #ifdef LED_RUN -// PIN_LED_ON; -// #endif - -// // --- HARDWARE INITIALIZATION --- -// PIN_DATA_OUTPUT; -// PIN_DATA_CLEAR; - -// if (!wfck_mode) { -// PIN_WFCK_OUTPUT; -// PIN_WFCK_CLEAR; -// } - -// _delay_ms(DELAY_BETWEEN_INJECTIONS); - -// // --- MAIN INJECTION LOOP (3 BURSTS) --- -// for (uint8_t i = 0; i < 3; i++) { - -// // Select region pattern: Cycle through all if injectSCEx == 3 -// uint8_t regionIndex = (injectSCEx == 3) ? i : injectSCEx; -// const uint8_t* bytePtr = allRegionsSCEx[regionIndex]; - -// // Branching here instead of inside the loops for assembly optimization -// if (!wfck_mode) { -// // --- LOGIC GATE MODE (PU-7 to PU-20) --- -// for (uint8_t b = 0; b < 6; b++) { -// uint8_t currentByte = bytePtr[b]; -// uint8_t bitsToProcess = (b == 5) ? 4 : 8; - -// while (bitsToProcess--) { -// if (currentByte & 0x01) { -// PIN_DATA_INPUT; // Logic '1' via High-Impedance -// } else { -// PIN_DATA_OUTPUT; // Logic '0' via Active Drive -// PIN_DATA_CLEAR; -// } -// currentByte >>= 1; -// _delay_us(DELAY_BETWEEN_BITS); -// } -// } -// } -// else { -// // --- WFCK MODULATION MODE (PU-22+) --- -// for (uint8_t b = 0; b < 6; b++) { -// uint8_t currentByte = bytePtr[b]; -// uint8_t bitsToProcess = (b == 5) ? 4 : 8; - -// while (bitsToProcess--) { -// if (currentByte & 0x01) { -// // Modulate '1' bit using WFCK clock edges (30 cycles) -// uint8_t count = 30; -// while (count--) { -// while (PIN_WFCK_READ); // Wait for LOW -// PIN_DATA_CLEAR; -// while (!PIN_WFCK_READ); // Wait for HIGH -// PIN_DATA_SET; -// } -// } else { -// // Static '0' bit -// PIN_DATA_CLEAR; -// _delay_us(DELAY_BETWEEN_BITS); -// } -// currentByte >>= 1; -// } -// } -// } - -// // --- INTER-STRING GAP --- -// PIN_DATA_OUTPUT; -// PIN_DATA_CLEAR; -// _delay_ms(DELAY_BETWEEN_INJECTIONS); -// } - -// // --- CLEANUP --- -// if (!wfck_mode) { -// PIN_WFCK_INPUT; -// PIN_DATA_INPUT; -// } -// #ifdef LED_RUN -// PIN_LED_OFF; -// #endif -// } - -// void performInjectionSequence(uint8_t injectSCEx) { -// // 44-bit SCEx patterns (6 bytes: 5 full + 4 bits) -// static const uint8_t allRegionsSCEx[3][6] = { -// { 0x59, 0xC9, 0x4B, 0x5D, 0xDA, 0x02 }, // SCEI -// { 0x59, 0xC9, 0x4B, 0x5D, 0xFA, 0x02 }, // SCEA -// { 0x59, 0xC9, 0x4B, 0x5D, 0xEA, 0x02 } // SCEE -// }; - -// hysteresis = 11; -// #ifdef LED_RUN -// PIN_LED_ON; -// #endif - -// // --- HARDWARE INITIALIZATION --- -// PIN_DATA_OUTPUT; -// PIN_DATA_CLEAR; - -// if (!wfck_mode) { -// PIN_WFCK_OUTPUT; -// PIN_WFCK_CLEAR; -// } - -// _delay_ms(DELAY_BETWEEN_INJECTIONS); - -// // --- MAIN INJECTION LOOP (3 BURSTS) --- -// for (uint8_t i = 0; i < 3; i++) { - -// // Select region pattern: Cycle through all if injectSCEx == 3 -// uint8_t regionIndex = (injectSCEx == 3) ? i : injectSCEx; -// const uint8_t* bytePtr = allRegionsSCEx[regionIndex]; - -// for (uint8_t b = 0; b < 6; b++) { -// uint8_t currentByte = bytePtr[b]; -// // On the 6th byte, only process 4 bits (Total 44 bits) -// uint8_t bits = (b == 5) ? 4 : 8; - -// while (bits--) { -// uint8_t currentBit = currentByte & 0x01; -// currentByte >>= 1; - -// if (currentBit == 0) { -// // --- LOGIC FOR '0' BIT (Universal) --- -// PIN_DATA_CLEAR; -// if (!wfck_mode) PIN_DATA_OUTPUT; -// _delay_us(DELAY_BETWEEN_BITS); -// } -// else { -// // --- LOGIC FOR '1' BIT --- -// if (!wfck_mode) { -// // OLD BOARDS: High Impedance -// PIN_DATA_INPUT; -// _delay_us(DELAY_BETWEEN_BITS); -// } -// else { -// // NEW BOARDS: WFCK Modulation (30 cycles) -// uint8_t count = 30; -// while (count--) { -// while (PIN_WFCK_READ); // Wait for LOW -// PIN_DATA_CLEAR; -// while (!PIN_WFCK_READ); // Wait for HIGH -// PIN_DATA_SET; -// } -// } -// } -// } -// } - -// // --- INTER-STRING GAP --- -// PIN_DATA_OUTPUT; -// PIN_DATA_CLEAR; -// _delay_ms(DELAY_BETWEEN_INJECTIONS); -// } - -// // --- CLEANUP --- -// if (!wfck_mode) { -// PIN_WFCK_INPUT; -// PIN_DATA_INPUT; -// } -// #ifdef LED_RUN -// PIN_LED_OFF; -// #endif -// } - - void performInjectionSequence(uint8_t injectSCEx) { + /* + Security strings (44-bit SCEx) for the three main regions: + 0: NTSC-J (SCEI - Sony Computer Entertainment Inc.) + 1: NTSC-U/C (SCEA - Sony Computer Entertainment America) + 2: PAL (SCEE - Sony Computer Entertainment Europe) + Stored in 6 bytes (48 bits); only the first 44 bits are used during injection. + */ static const uint8_t allRegionsSCEx[3][6] = { - { 0x59, 0xC9, 0x4B, 0x5D, 0xDA, 0x02 }, - { 0x59, 0xC9, 0x4B, 0x5D, 0xFA, 0x02 }, - { 0x59, 0xC9, 0x4B, 0x5D, 0xEA, 0x02 } + { 0x59, 0xC9, 0x4B, 0x5D, 0xDA, 0x02 }, // SCEI + { 0x59, 0xC9, 0x4B, 0x5D, 0xFA, 0x02 }, // SCEA + { 0x59, 0xC9, 0x4B, 0x5D, 0xEA, 0x02 } // SCEE }; - hysteresis = 11; + + hysteresis = 11; // Reset hysteresis to mid-point after triggering #ifdef LED_RUN PIN_LED_ON; #endif + // Cache wfck_mode to save CPU cycles during the bit loop uint8_t isWfck = wfck_mode; PIN_DATA_OUTPUT; PIN_DATA_CLEAR; + // Legacy boards require the chip to drive the simulated WFCK/Gate if (!isWfck) { PIN_WFCK_OUTPUT; PIN_WFCK_CLEAR; @@ -803,6 +409,7 @@ void performInjectionSequence(uint8_t injectSCEx) { _delay_ms(DELAY_BETWEEN_INJECTIONS); + // Loop through the selected region(s) for (uint8_t i = 0; i < 3; i++) { uint8_t regionIndex = (injectSCEx == 3) ? i : injectSCEx; const uint8_t* bytePtr = allRegionsSCEx[regionIndex]; @@ -810,7 +417,7 @@ void performInjectionSequence(uint8_t injectSCEx) { uint8_t currentByte = *bytePtr++; uint8_t bitIdx = 0; - // OPTIMIZATION: One single loop for all 44 bits + // Process the 44-bit SCEx stream for (uint8_t totalBits = 44; totalBits > 0; totalBits--) { uint8_t currentBit = currentByte & 0x01; currentByte >>= 1; @@ -823,30 +430,46 @@ void performInjectionSequence(uint8_t injectSCEx) { } if (currentBit == 0) { + // BIT 0: Pull DATA low- PIN_DATA_CLEAR; if (!isWfck) PIN_DATA_OUTPUT; _delay_us(DELAY_BETWEEN_BITS); } else { + // BIT 1: Handle based on board generation if (!isWfck) { + // Legacy Mode: Set DATA to High-Z (floating) PIN_DATA_INPUT; _delay_us(DELAY_BETWEEN_BITS); } else { + /* + WFCK Modulation Loop: Syncs to 7.3kHz or 14.6kHz. + Follows hardware edges to stay bit-perfect with the console. + */ uint8_t count = 30; while (count--) { - while (PIN_WFCK_READ); + while (PIN_WFCK_READ); // Wait for Falling Edge PIN_DATA_CLEAR; - while (!PIN_WFCK_READ); + while (!PIN_WFCK_READ); // Wait for Rising Edge PIN_DATA_SET; } } } } + // Clean up state between region cycles PIN_DATA_OUTPUT; PIN_DATA_CLEAR; _delay_ms(DELAY_BETWEEN_INJECTIONS); + + /* + EXIT CONDITION: + If we are NOT in Universal mode (3), we stop after the first + successful region injection. + */ + if (injectSCEx != 3) break; } + // Restore pins to Input/High-Z to avoid signal interference after injection if (!isWfck) { PIN_WFCK_INPUT; PIN_DATA_INPUT; @@ -866,6 +489,7 @@ void performInjectionSequence(uint8_t injectSCEx) { + void Init() { #if defined(ATmega328_168) @@ -876,14 +500,16 @@ void Init() { PIN_LED_OUTPUT; #endif -#ifdef BIOS_PATCH_2 - uint8_t Flag_Switch = 0; +#ifdef BIOS_PATCH + uint8_t skipPatch = 0; GLOBAL_INTERRUPT_ENABLE; + #ifdef SCPH_7000 + // Check hardware switch for SCPH-7000 models PIN_SWITCH_INPUT; PIN_SWITCH_SET; if (PIN_SWITCH_READ == 0){ - Flag_Switch =1; + skipPatch =1; // Disable patching if switch is triggered } #endif @@ -891,18 +517,19 @@ void Init() { PIN_LED_ON; #endif - if (Flag_Switch == 0) { + // Execute BIOS patching unless bypassed by switch + if (skipPatch == 0) { Bios_Patching(); } #ifdef LED_RUN PIN_LED_OFF; #endif - #endif + + // Disable interrupts and set CD-ROM interface pins to Input GLOBAL_INTERRUPT_DISABLE; - PIN_SQCK_INPUT; PIN_SUBQ_INPUT; @@ -913,6 +540,7 @@ void Init() { Serial.begin(500000); // 60 bytes in 12ms (expected data: ~26 bytes / 12ms) // update: this is actually quicker #endif + // Detect board generation (PU-7 to PU-22+) before starting the main loop board_detection(); } @@ -929,28 +557,25 @@ int main() { 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*/ - captureSubQ(); + // Small delay to prevent re-reading the tail end of the same SUBQ packet + _delay_ms(1); - /*------------------------------------------------------------------------------- - Check if read head is in wobble area - We only want to unlock game discs (0x41) and only if the read head is in the outer TOC area. - We want to see a TOC sector repeatedly before injecting (helps with timing and marginal lasers). - All this logic is because we don't know if the HC-05 is actually processing a getSCEX() command. - Hysteresis is used because older drives exhibit more variation in read head positioning. - While the laser lens moves to correct for the error, they can pick up a few TOC sectors. - -------------------------------------------------------------------------------*/ + //Capture the 12-byte Sub-Q channel data into subqBuffer + captureSubQ(); // Optimized Sector Filtering: // Masking bits 7, 6, and 4 simultaneously using 0xD0 (binary 11010000). // This verifies that the "Data/TOC" bit (0x40) is SET, while bits 7 and 4 are CLEARED. // Equivalent to: (bit7 == 0 && bit6 == 1 && bit4 == 0). - uint8_t isDataSector = ((scbuf[0] & 0xD0) == 0x40); + //uint8_t isDataSector = ((scbuf[0] & 0xD0) == 0x40); + + uint8_t isDataSector = ((subqBuffer[0] & 0xD0) == 0x40); + // Execute selected logic through function pointer - currentLogic(isDataSector); + processStandardLogic(isDataSector); + //Trigger SCEx injection once the confidence threshold is reached if (hysteresis >= HYSTERESIS_MAX) { performInjectionSequence(INJECT_SCEx); } diff --git a/PSNee/settings.h b/PSNee/settings.h index f62bb75..41554f3 100644 --- a/PSNee/settings.h +++ b/PSNee/settings.h @@ -157,32 +157,39 @@ void Debug_Log (int Wfck_mode){ //Information about the MCU, and old or #endif } - // log SUBQ packets. We only have 12ms to get the logs written out. Slower MCUs get less formatting. -void Debug_Scbuf (uint8_t *Scbuf){ // Data from the DATA bus +// Logs SUBQ packets to serial. We only have 12ms to write logs before the next packet. +// Slower MCUs (like ATtiny) receive minimal formatting to save cycles. +void logSubQ(uint8_t *dataBuffer) { + + // A bad sector read results in zeros (except for CRC). Skip logging if first 4 bytes are 0. + if (!(dataBuffer[0] == 0 && dataBuffer[1] == 0 && dataBuffer[2] == 0 && dataBuffer[3] == 0)) { + #if defined(ATtiny85_45_25) - if (!(Scbuf[0] == 0 && Scbuf[1] == 0 && Scbuf[2] == 0 && Scbuf[3] == 0)) { // a bad sector read is all 0 except for the CRC fields. Don't log it. - for (int i = 0; i < 12; i++) { - if (Scbuf[i] < 0x10) { - mySerial.print("0"); // padding + // Compact formatting for ATtiny to meet the 12ms timing constraint + for (uint8_t i = 0; i < 12; i++) { + if (dataBuffer[i] < 0x10) { + mySerial.print("0"); // Leading zero padding } - mySerial.print(Scbuf[i, HEX]); + mySerial.print(dataBuffer[i], HEX); } - mySerial.println(""); - } -#elif !defined(ATtiny85_45_25) - if (!(Scbuf[0] == 0 && Scbuf[1] == 0 && Scbuf[2] == 0 && Scbuf[3] == 0)) { - for (int i = 0; i < 12; i++) { - if (Scbuf[i] < 0x10) { - Serial.print("0"); // padding + mySerial.println(""); + +#else + // Standard formatting with spaces for more powerful MCUs + for (uint8_t i = 0; i < 12; i++) { + if (dataBuffer[i] < 0x10) { + Serial.print("0"); // Leading zero padding } - Serial.print(Scbuf[i], HEX); + Serial.print(dataBuffer[i], HEX); Serial.print(" "); } Serial.println(""); - } #endif + + } } + void Debug_Inject(){ // Confirmation of region code injection #if defined(ATtiny85_45_25)