mirror of
https://github.com/kalymos/PsNee.git
synced 2026-05-08 16:32:17 +00:00
Refactor: optimized hysteresis coupling and main loop cleanup
Logic: Fine-tuned hysteresis reset to (HYSTERESIS_MAX - 6) for improved stability on sensitive SCPH-100x models. Main: Finalized main loop cleanup by centralizing Data/TOC validation within FilterSUBQSamples. Docs: Standardized all technical headers and comments for
This commit is contained in:
214
PSNee/PSNee.ino
214
PSNee/PSNee.ino
@@ -106,13 +106,13 @@
|
||||
#include "MCU.h"
|
||||
#include "settings.h"
|
||||
|
||||
uint8_t wfck_mode = 0; //Flag initializing for automatic console generation selection 0 = old, 1 = pu-22 end ++
|
||||
uint8_t SUBQBuffer[12]; // Global buffer to store the 12-byte SUBQ channel data
|
||||
|
||||
#define HYSTERESIS_MAX 25 // Now coupled with post-injection reset; allows for higher
|
||||
// initial accumulation targets without the alignment drift
|
||||
// (desync) previously affecting SCPH-100x models.
|
||||
|
||||
uint8_t wfck_mode = 0; //Flag initializing for automatic console generation selection 0 = old, 1 = pu-22 end ++
|
||||
uint8_t SUBQBuffer[12]; // Global buffer to store the 12-byte SUBQ channel data
|
||||
|
||||
uint8_t hysteresis = 0;
|
||||
|
||||
#if defined(DEBUG_SERIAL_MONITOR)
|
||||
@@ -123,9 +123,8 @@ uint8_t hysteresis = 0;
|
||||
* Code section
|
||||
********************************************************************************************************************/
|
||||
/****************************************************************************************
|
||||
* FUNCTION : Bios_Patching()
|
||||
* **************************************************************************************
|
||||
*
|
||||
* FUNCTION : Bios_Patching()
|
||||
*
|
||||
* OPERATION : Real-time Data Bus (DX) override via Address Bus (AX / AY)
|
||||
*
|
||||
* KEY PHASES:
|
||||
@@ -324,45 +323,30 @@ uint8_t hysteresis = 0;
|
||||
* DESCRIPTION :
|
||||
* Distinguishes motherboard generations (PU-7 through PU-22+) by analyzing
|
||||
* the behavior of the WFCK signal.
|
||||
* SIGNAL CHARACTERISTICS:
|
||||
*
|
||||
* SIGNAL CHARACTERISTICS:
|
||||
* - Legacy Boards (PU-7 to PU-20): WFCK acts as a static GATE signal.
|
||||
* It remains HIGH (continuous) during the region-check window.
|
||||
* It remains HIGH.
|
||||
* - Modern Boards (PU-22 or newer): WFCK is an oscillating clock signal
|
||||
* (Frequency-based).
|
||||
*
|
||||
*
|
||||
* WFCK: __----------------------- // CONTINUOUS (PU-7 .. PU-20)(GATE)
|
||||
*
|
||||
* WFCK: __-_-_-_-_-_-_-_-_-_-_-_- // FREQUENCY (PU-22 or newer)
|
||||
*
|
||||
*
|
||||
* HISTORICAL CONTEXT:
|
||||
* HISTORICAL CONTEXT:
|
||||
* Traditionally, WFCK was referred to as the "GATE" signal. On early models,
|
||||
* modchips functioned as a synchronized gate, pulling the signal LOW
|
||||
* precisely when the region-lock data was being processed.
|
||||
*
|
||||
* FREQUENCY DATA:
|
||||
* FREQUENCY DATA:
|
||||
* - Initial/Protection Phase: ~7.3 kHz.
|
||||
* - Standard Data Reading: ~14.6 kHz.
|
||||
*
|
||||
*******************************************************************************************************************/
|
||||
|
||||
/**
|
||||
* FUNCTION : BoardDetection
|
||||
*
|
||||
* DESCRIPTION :
|
||||
* Distinguishes motherboard generations (PU-7 through PU-22+) by analyzing
|
||||
* the behavior of the WFCK signal.
|
||||
*
|
||||
* SIGNAL CHARACTERISTICS:
|
||||
* - Legacy Boards (PU-7 to PU-20): WFCK acts as a static GATE signal.
|
||||
* It remains HIGH (continuous) during the region-check window.
|
||||
* - Modern Boards (PU-22 or newer): WFCK is an oscillating clock signal
|
||||
* (Frequency-based).
|
||||
*
|
||||
* WFCK: __----------------------- // CONTINUOUS (PU-7 .. PU-20)(GATE)
|
||||
*
|
||||
* WFCK: __-_-_-_-_-_-_-_-_-_-_-_- // FREQUENCY (PU-22 or newer)
|
||||
*/
|
||||
void BoardDetection() {
|
||||
wfck_mode = 0; // Default: Legacy (GATE)
|
||||
uint8_t pulse_hits = 25; // We need to see 25 oscillations to confirm FREQUENCY mode
|
||||
@@ -417,30 +401,30 @@ void CaptureSUBQ(void) {
|
||||
uint8_t* bufferPtr = SUBQBuffer;
|
||||
|
||||
do {
|
||||
uint8_t currentByte = 0;
|
||||
uint8_t bitsToRead = 8;
|
||||
|
||||
// Direct byte initialization to save a register copy
|
||||
*bufferPtr = 0;
|
||||
|
||||
while (bitsToRead--) {
|
||||
/**
|
||||
* PHASE 1: BIT SYNCHRONIZATION (SQCK)
|
||||
* The CD controller signals a new bit by toggling the Clock line.
|
||||
* Data is sampled on the RISING EDGE of SQCK for maximum stability.
|
||||
* Wait for falling edge then sample on the RISING EDGE of SQCK.
|
||||
*/
|
||||
while (PIN_SQCK_READ); // Wait for falling edge
|
||||
while (!PIN_SQCK_READ); // Wait for rising edge
|
||||
|
||||
/**
|
||||
* PHASE 2: BIT ACQUISITION & SHIFTING
|
||||
* The PlayStation SUBQ bus transmits data LSB (Least Significant Bit) first.
|
||||
* We shift the current byte right and inject the new bit into the MSB (0x80).
|
||||
* LSB first: shift the buffer pointer directly to avoid 'currentByte' overhead.
|
||||
*/
|
||||
currentByte >>= 1;
|
||||
*bufferPtr >>= 1;
|
||||
if (PIN_SUBQ_READ) {
|
||||
currentByte |= 0x80;
|
||||
*bufferPtr |= 0x80;
|
||||
}
|
||||
}
|
||||
// Store reconstructed byte and advance pointer
|
||||
*bufferPtr++ = currentByte;
|
||||
// Advance pointer for the next byte
|
||||
bufferPtr++;
|
||||
|
||||
} while (--bytesRemaining); // Efficient countdown for AVR binary size
|
||||
|
||||
@@ -449,7 +433,6 @@ void CaptureSUBQ(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************************
|
||||
* FUNCTION : Filter_SUBQ_Samples() [SCPH_5903 Dual-Interface Variant]
|
||||
*
|
||||
@@ -468,38 +451,40 @@ void CaptureSUBQ(void) {
|
||||
******************************************************************************************/
|
||||
#ifdef SCPH_5903
|
||||
|
||||
void FilterSUBQSamples(uint8_t isDataSector) {
|
||||
uint8_t currentHysteresis = hysteresis;
|
||||
void FilterSUBQSamples(uint8_t controlByte) {
|
||||
|
||||
// --- STEP 0: Data/TOC Validation ---
|
||||
// Optimized mask (0xD0) to verify bit6 is SET while bit7 and bit4 are CLEARED.
|
||||
uint8_t isDataSector = ((controlByte & 0xD0) == 0x40);
|
||||
|
||||
// --- STEP 1: SUBQ Frame Synchronization ---
|
||||
// Fast filtering: ignore raw data if sync markers (index 1 and 6) are not 0x00.
|
||||
if (SUBQBuffer[1] == 0x00 && SUBQBuffer[6] == 0x00) {
|
||||
uint8_t pointAddress = SUBQBuffer[2];
|
||||
// --- STEP 1: SUBQ Frame Synchronization ---
|
||||
// Fast filtering: ignore raw data if sync markers (index 1 and 6) are not 0x00.
|
||||
if (SUBQBuffer[1] == 0x00 && SUBQBuffer[6] == 0x00) {
|
||||
|
||||
/**
|
||||
* HIT INCREMENT CONDITIONS:
|
||||
* A. VALID PSX LEAD-IN: Data sector AND Point A0-A2 range AND NOT VCD (sub-mode != 0x02).
|
||||
* (uint8_t)(pointAddress - 0xA0) <= 2 is an optimized check for 0xA0, 0xA1, 0xA2.
|
||||
* B. TRACKING MAINTENANCE: Keeps count if already synced and reading Mode 0x01 or Data.
|
||||
*/
|
||||
if ( (isDataSector && (uint8_t)(pointAddress - 0xA0) <= 2 && SUBQBuffer[3] != 0x02) ||
|
||||
(currentHysteresis > 0 && (SUBQBuffer[0] == 0x01 || isDataSector)) )
|
||||
{
|
||||
hysteresis = currentHysteresis + 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* HIT INCREMENT CONDITIONS:
|
||||
* A. VALID PSX LEAD-IN: Data sector AND Point A0-A2 range AND NOT VCD (sub-mode != 0x02).
|
||||
* (uint8_t)(SUBQBuffer[2] - 0xA0) <= 2 is an optimized check for 0xA0, 0xA1, 0xA2.
|
||||
* B. TRACKING MAINTENANCE: Keeps count if already synced and reading Mode 0x01 or Data.
|
||||
*/
|
||||
if ( (isDataSector && (uint8_t)(SUBQBuffer[2] - 0xA0) <= 2 && SUBQBuffer[3] != 0x02) ||
|
||||
(hysteresis > 0 && (SUBQBuffer[0] == 0x01 || isDataSector)) )
|
||||
{
|
||||
hysteresis++; // Direct increment: faster on 16MHz AVR
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// --- STEP 2: Signal Decay / Pattern Mismatch ---
|
||||
// Decrement the hit counter if no valid PSX pattern is detected in the SUBQ stream.
|
||||
if (currentHysteresis > 0) {
|
||||
hysteresis = currentHysteresis - 1;
|
||||
}
|
||||
// --- STEP 2: Signal Decay / Pattern Mismatch ---
|
||||
// Decrement the hit counter if no valid PSX pattern is detected in the SUBQ stream.
|
||||
if (hysteresis > 0) {
|
||||
hysteresis--; // Direct decrement: saves CPU cycles
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
/******************************************************************************************
|
||||
* FUNCTION : Filter_SUBQ_Samples()
|
||||
* FUNCTION : FilterSUBQSamples()
|
||||
*
|
||||
* DESCRIPTION :
|
||||
* Parses and filters the raw serial data stream from the SUBQ pin.
|
||||
@@ -514,64 +499,61 @@ void CaptureSUBQ(void) {
|
||||
* INPUT : isDataSector (bool) - Filtered flag based on raw sector control bits.
|
||||
******************************************************************************************/
|
||||
|
||||
void FilterSUBQSamples(uint8_t isDataSector) {
|
||||
uint8_t currentHysteresis = hysteresis;
|
||||
void FilterSUBQSamples(uint8_t controlByte) {
|
||||
|
||||
// --- STEP 0: Data/TOC Validation ---
|
||||
// Optimized mask (0xD0) to verify bit6 is SET while bit7 and bit4 are CLEARED.
|
||||
uint8_t isDataSector = ((controlByte & 0xD0) == 0x40);
|
||||
|
||||
// --- STEP 1: SUBQ Frame Synchronization ---
|
||||
// Ignore the raw bitstream unless sync markers (1 & 6) are 0x00.
|
||||
if (SUBQBuffer[1] == 0x00 && SUBQBuffer[6] == 0x00) {
|
||||
uint8_t pointAddress = SUBQBuffer[2];
|
||||
|
||||
// uint8_t syncMarker1 = SUBQBuffer[1];
|
||||
// uint8_t syncMarker2 = SUBQBuffer[6];
|
||||
|
||||
// if (syncMarker1 == 0x00 && syncMarker2 == 0x00) {
|
||||
// uint8_t addrControl = SUBQBuffer[0];
|
||||
// uint8_t pointAddress = SUBQBuffer[2];
|
||||
// uint8_t trackNumber = SUBQBuffer[3];
|
||||
|
||||
/*
|
||||
* HIT INCREMENT CONDITIONS:
|
||||
* A. LEAD-IN PATTERNS: Detects TOC markers (A0-A2) or Track 01 at spiral start.
|
||||
* (uint8_t)(SUBQBuffer[3] - 0x03) >= 0xF5 handles the 0x98 to 0x02 wrap-around.
|
||||
* B. TRACKING LOCK: Maintains count if already synced and reading valid sectors.
|
||||
*/
|
||||
if ( (isDataSector && (pointAddress >= 0xA0 || (pointAddress == 0x01 && ( (uint8_t)(SUBQBuffer[3] - 0x03) >= 0xF5)))) ||
|
||||
(currentHysteresis > 0 && (SUBQBuffer[0] == 0x01 || isDataSector)) )
|
||||
* HIT INCREMENT CONDITIONS:
|
||||
* A. LEAD-IN PATTERNS: Detects TOC markers (A0-A2) or Track 01 at spiral start.
|
||||
* (uint8_t)(SUBQBuffer[3] - 0x03) >= 0xF5 handles the 0x98 to 0x02 wrap-around.
|
||||
* B. TRACKING LOCK: Maintains count if already synced and reading valid sectors.
|
||||
*/
|
||||
if ( (isDataSector && (SUBQBuffer[2] >= 0xA0 || (SUBQBuffer[2] == 0x01 && ( (uint8_t)(SUBQBuffer[3] - 0x03) >= 0xF5)))) ||
|
||||
(hysteresis > 0 && (SUBQBuffer[0] == 0x01 || isDataSector)) )
|
||||
{
|
||||
hysteresis = currentHysteresis + 1;
|
||||
hysteresis++; // Direct increment: saves CPU cycles
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// --- STEP 2: Signal Decay / Missed Hits ---
|
||||
// Reduce the hit counter if the current SUBQ sample fails validation.
|
||||
if (currentHysteresis > 0) {
|
||||
hysteresis = currentHysteresis - 1;
|
||||
if (hysteresis > 0) {
|
||||
hysteresis--; // Direct decrement: faster on 16MHz AVR
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
/*********************************************************************************************
|
||||
* Executes the SCEx injection sequence to bypass the CD-ROM regional lockout.
|
||||
* FUNCTION : PerformInjectionSequence
|
||||
*
|
||||
* 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.
|
||||
* DESCRIPTION :
|
||||
* Executes the SCEx injection sequence to bypass the CD-ROM regional lockout.
|
||||
* 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 (7.3 kHz or 14.6 kHz).
|
||||
*
|
||||
* 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.
|
||||
* 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) {
|
||||
/*
|
||||
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)
|
||||
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] = {
|
||||
@@ -582,10 +564,13 @@ void PerformInjectionSequence(uint8_t injectSCEx) {
|
||||
|
||||
const uint16_t BIT_DELAY = 4000; // 4000us is the standard bit timing for SCEx signal (approx. 250 bps)
|
||||
const uint8_t isWfck = wfck_mode; // Cache wfck_mode to save CPU cycles during the bit loop
|
||||
|
||||
hysteresis = (HYSTERESIS_MAX - 6); // Coupled HYSTERESIS_MAX with hysteresis-reset post-injection; allows for a
|
||||
// wider tuning range of HYSTERESIS_MAX without drifting out of the sweet spot
|
||||
// between hysteresis_max and hysteresis_reset.
|
||||
/**
|
||||
* HYSTERESIS COUPLING:
|
||||
* Resets hysteresis close to MAX after injection. This allows for a wider
|
||||
* tuning range of HYSTERESIS_MAX without drifting out of synchronization
|
||||
* on SCPH-100x models.
|
||||
*/
|
||||
hysteresis = (HYSTERESIS_MAX - 6);
|
||||
|
||||
#ifdef LED_RUN
|
||||
PIN_LED_ON;
|
||||
@@ -647,11 +632,7 @@ void PerformInjectionSequence(uint8_t injectSCEx) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
EXIT CONDITION:
|
||||
If we are NOT in Universal mode (3), we stop after the first
|
||||
successful region injection.
|
||||
*/
|
||||
// EXIT CONDITION: Stop after the first successful region unless Universal mode (3)
|
||||
if (injectSCEx != 3) {
|
||||
|
||||
PIN_DATA_OUTPUT;
|
||||
@@ -665,7 +646,7 @@ void PerformInjectionSequence(uint8_t injectSCEx) {
|
||||
}
|
||||
|
||||
|
||||
// Clean up state between region cycles
|
||||
// Inter-region delay for Universal mode
|
||||
PIN_DATA_OUTPUT;
|
||||
PIN_DATA_CLEAR;
|
||||
_delay_ms(180);
|
||||
@@ -727,8 +708,6 @@ void Init() {
|
||||
Serial.begin(500000); // Standard hardware UART for 328/168
|
||||
#endif
|
||||
|
||||
|
||||
// --- Console Analysis ---
|
||||
// Identify board revision (PU-7 to PU-22+) to set correct injection timings
|
||||
BoardDetection();
|
||||
}
|
||||
@@ -738,32 +717,23 @@ int main() {
|
||||
Init();
|
||||
|
||||
#if defined(DEBUG_SERIAL_MONITOR)
|
||||
// Display initial board detection results (Window remaining & WFCK mode)
|
||||
BoardDetectionLog( global_window, wfck_mode, INJECT_SCEx);
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
|
||||
// 1. Timing Sync: Prevent reading the tail end of the previous SUBQ packet
|
||||
// Timing Sync: Prevent reading the tail end of the previous SUBQ packet
|
||||
_delay_ms(1);
|
||||
|
||||
// 2. Data Acquisition: Capture 12-byte SUBQ channel stream into buffer
|
||||
// Data Acquisition: Capture 12-byte SUBQ channel stream into buffer
|
||||
CaptureSUBQ();
|
||||
|
||||
// 3. Sector Validation (Data/TOC check):
|
||||
// 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 firstByte = SUBQBuffer[0];
|
||||
// uint8_t isDataSector = ((firstByte & 0xD0) == 0x40);
|
||||
uint8_t isDataSector = ((SUBQBuffer[0] & 0xD0) == 0x40);
|
||||
// Confidence Filtering: Accumulate hits toward HYSTERESIS_MAX
|
||||
// Validation and Data/TOC masking (0xD0) are handled inside the filter.
|
||||
FilterSUBQSamples(SUBQBuffer[0]);
|
||||
|
||||
|
||||
// Execute selected logic
|
||||
FilterSUBQSamples(isDataSector);
|
||||
|
||||
// 5. Execution: Trigger SCEx injection once confidence (hysteresis) is reached
|
||||
// uint8_t currentHysteresis = hysteresis;
|
||||
// if (currentHysteresis >= HYSTERESIS_MAX) {
|
||||
// Execution: Trigger SCEx injection once confidence (hysteresis) is reached
|
||||
if (hysteresis >= HYSTERESIS_MAX) {
|
||||
PerformInjectionSequence(INJECT_SCEx);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user