1
0
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:
kalymos
2026-03-22 16:21:10 +01:00
parent d2eb3e395d
commit d8f483e3a8

View File

@@ -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);
}