diff --git a/PSNee/BIOS_patching.h b/PSNee/BIOS_patching.h index 3f94fcc..b588288 100644 --- a/PSNee/BIOS_patching.h +++ b/PSNee/BIOS_patching.h @@ -11,21 +11,21 @@ * delays to inject modified data onto the Data line (DX) in real-time. * * KEY PHASES: - * 1. STABILIZATION & ALIGNMENT (AX): Synchronizes execution with a clean rising + * 1. STABILIZATION & ALIGNMENT (AX): Synchronizes execution with a clean rising * edge of the AX signal to establish a deterministic timing reference. * - * 2. SILENCE DETECTION (GATING): Scans for a specific window of bus inactivity - * (SILENCE_THRESHOLD) to ensure the system is at the correct pre-patching stage. + * 2. ADDRESS CALL DETECTION (PHASE 2): Scans the bus for specific address calls + * by validating consecutive polling blocks (SILENCE_THRESHOLD). * - * 3. HARDWARE PULSE COUNTING (ISR): Switches to interrupt-driven logic (INT0/INT1) - * to count address pulses with minimal latency and high-priority execution. + * 3. SILENCE CONFIRMATION (GATING): Counts the exact number of validated silence + * windows (CONFIRM_COUNTER_TARGET) to reach the correct pre-patching stage. * - * 4. DATA OVERDRIVE (DX): Upon reaching the target pulse, triggers a calibrated - * delay (BIT_OFFSET) and momentarily forces the DX pin to OUTPUT mode to + * 4. HARDWARE PULSE COUNTING (ISR): Uses INT0/INT1 to decrement the 'impulse' + * counter on each rising edge with minimal hardware latency. + * + * 5. DATA OVERDRIVE (DX): Upon reaching the target pulse, triggers a calibrated + * delay (BIT_OFFSET) and momentarily forces the DX pin to OUTPUT mode to * overwrite the BIOS bit for a precise duration (OVERRIDE). - * - * 5. SECONDARY PATCH (AY): If enabled, repeats the silence/counting sequence - * on a secondary address line (AY) for multi-stage memory patching. * ====================================================================================== */ @@ -35,12 +35,12 @@ * Shared state variables between ISRs and the main patching loop. * Declared 'volatile' to prevent compiler optimization during busy-wait loops. */ -volatile uint8_t impulse = 0; // Current address pulse countdown +volatile uint8_t impulse = 0; // Down-counter for physical address pulses volatile uint8_t patch = 0; // Synchronization flag (0: Idle, 1: AX Done, 2: AY Done) /** * PHASE 3: Primary Interrupt Service Routine (AX) - * Executes the real-time override on the target address cycle. + * Triggered on rising edges to perform the real-time bus override. */ ISR(PIN_AX_INTERRUPT_VECTOR) { if (--impulse == 0) { @@ -89,10 +89,26 @@ ISR(PIN_AY_INTERRUPT_VECTOR) { #endif void Bios_Patching(void) { - uint8_t current_confirms = 0; - uint16_t count; - patch = 0; + // --- HARDWARE BYPASS OPTION (SCPH-7000 specific) --- + #if defined(SCPH_7000) + PIN_SWITCH_INPUT; // Configure Pin D5 as Input + PIN_SWITCH_SET; // Enable internal Pull-up (D5 defaults to HIGH) + __builtin_avr_delay_cycles(2); // Short delay for voltage stabilization + + /** + * Exit immediately if the switch pulls the pin to GND (Logic LOW). + * This allows the user to disable the BIOS patch on-the-fly. + */ + if (PIN_SWITCH_READ == 0) { + return; + } + #endif + + uint8_t current_confirms = 0; + //uint16_t count; + + patch = 0; // Reset sync flag sei(); // Enable Global Interrupts PIN_AX_INPUT; // Set AX to monitor mode @@ -106,9 +122,9 @@ void Bios_Patching(void) { } // --- PHASE 2: SILENCE DETECTION --- - // Accumulate validated silence blocks to filter boot noise. + // Validate the exact number of silence windows to identify the boot stage. while (current_confirms < CONFIRM_COUNTER_TARGET) { - count = SILENCE_THRESHOLD; + uint16_t count = SILENCE_THRESHOLD; while (count > 0) { if (PIN_AX_READ != 0) { while (WAIT_AX_FALLING); @@ -117,7 +133,7 @@ void Bios_Patching(void) { count--; } if (count == 0) { - current_confirms++; // Silence block confirmed + current_confirms++; // Validated one silence window } } diff --git a/PSNee/MCU.h b/PSNee/MCU.h index 57d6f74..67b1352 100644 --- a/PSNee/MCU.h +++ b/PSNee/MCU.h @@ -225,8 +225,19 @@ #pragma once #ifdef ATmega328_168 - - static inline void optimizePeripherals(void) { + /*------------------------------------------------------------------------------------------------ + * FUNCTION : optimizePeripherals() + * + * DESCRIPTION : + * Minimizes power consumption and internal noise by disabling unused hardware modules. + * Performs a hard shutdown of peripherals to reduce current leakage and CPU jitter. + * + * 1. ANALOG SHUTDOWN: Disables ADC and Analog Comparator to prevent parasitic drain. + * 2. BUFFER DISABLE: Disconnects digital input buffers on analog pins (A0-A5). + * 3. CLOCK GATING (PRR): Shuts down clocks to TWI, SPI, and all Timers (0, 1, 2). + * 4. UART MAINTENANCE: Keeps PRUSART0 active to allow Serial communication. + ------------------------------------------------------------------------------------------------*/ + static inline void OptimizePeripherals(void) { // 1. Disable Interrupts during setup cli(); @@ -275,59 +286,57 @@ #define PIN_SQCK_INPUT DDRD &= ~(1 << DDD6) // Set DDRB register to configure PINB6 as input #define PIN_SUBQ_INPUT DDRD &= ~(1 << DDD7) // Set DDRB register to configure PINB7 as input - // Enable pull-ups and set high on the main pins + // Configure lines as outputs (for injection/override) #define PIN_DATA_OUTPUT DDRB |= (1 << DDB0) // Set DDRB register to configure PINB0 as output #define PIN_WFCK_OUTPUT DDRB |= (1 << DDB1) // Set DDRB register to configure PINB1 as output - // Define pull-ups and set high at the main pin - #define PIN_DATA_SET PORTB |= (1 << PB0) // Set PORTB register to make PINB0 high (enable pull-up) - - // Clear the main pins (set low) + // Bus line state control (Set High / Clear Low) + #define PIN_DATA_SET PORTB |= (1 << PB0) // Set PORTB register to make PINB0 high (enable pull-up)) #define PIN_DATA_CLEAR PORTB &= ~(1 << PB0) // Set PORTB register to make PINB0 low #define PIN_WFCK_CLEAR PORTB &= ~(1 << PB1) // Set PORTB register to make PINB1 low - // Read the state of the main input pins + // Direct Register Reading (High-speed polling) #define PIN_SQCK_READ (!!(PIND & (1 << PIND6))) // Check if the value of PIND6 is high (1) #define PIN_SUBQ_READ (!!(PIND & (1 << PIND7))) // Check if the value of PIND7 is high (1) #define PIN_WFCK_READ (!!(PINB & (1 << PINB1))) // Check if the value of PIND1 is high (1) - // LED pin handling (for indication) + // --- Status Indication (LED) --- #ifdef LED_RUN #define PIN_LED_OUTPUT DDRB |= (1 << DDB5) // Configure PINB5 as output (for LED) #define PIN_LED_ON PORTB |= (1 << PB5) // Set PINB5 high (turn on LED) #define PIN_LED_OFF PORTB &= ~(1 << PB5) // Set PINB5 low (turn off LED) #endif - // Handling the BIOS patch - #if defined(SCPH_102) || defined(SCPH_100) || defined(SCPH_7500_9000) || defined(SCPH_7000) || defined(SCPH_5000_5500) || defined(SCPH_3500) || defined(SCPH_3000) || defined(SCPH_1000) - - // Define input pins for the BIOS patch - #define PIN_AX_INPUT DDRD &= ~(1 << DDD2) // Set DDRD register to configure PIND2 as input - #define PIN_DX_INPUT DDRD &= ~(1 << DDD4) // Set DDRD register to configure PIND4 as input - - // Define output pins for the BIOS patch - #define PIN_DX_OUTPUT DDRD |= (1 << DDD4) // Set DDRD register to configure PIND4 as output - - // Set pull-ups high on output pins - #define PIN_DX_SET PORTD |= (1 << PD4) // Set PORTD register to make PIND4 high - - // Set pull-ups low on output pins - #define PIN_DX_CLEAR PORTD &= ~(1 << PD4) // Set PORTD register to make PIND4 low + // --- BIOS Patching Configuration --- + #if defined(SCPH_102) || \ + defined(SCPH_100) || \ + defined(SCPH_7500_9000) || \ + defined(SCPH_7000) || \ + defined(SCPH_3500_5500) || \ + defined(SCPH_3000) || \ + defined(SCPH_1000) + // Address (AX) and Data (DX) lines for BIOS override + #define PIN_AX_INPUT DDRD &= ~(1 << DDD2) + #define PIN_DX_INPUT DDRD &= ~(1 << DDD4) + #define PIN_DX_OUTPUT DDRD |= (1 << DDD4) + #define PIN_DX_SET PORTD |= (1 << PD4) + #define PIN_DX_CLEAR PORTD &= ~(1 << PD4) + // Blocking wait macros for AX synchronization #define WAIT_AX_RISING (!(PIND & (1 << PIND2))) // Wait for pulse start (Blocking until Rising Edge) #define WAIT_AX_FALLING (PIND & (1 << PIND2)) // Wait for pulse end (Blocking until Falling Edge) + #define PIN_AX_READ (!!(PIND & (1 << PIND2))) - // Read the input pins for the BIOS patch - #define PIN_AX_READ (!!(PIND & (1 << PIND2))) // Read the state of PIND2 - + // Hardware Interrupt (INT0) for AX pulse counting #define PIN_AX_INTERRUPT_ENABLE EIMSK |= (1< #include - // Globale interrupt seting - #define GLOBAL_INTERRUPT_ENABLE SREG |= (1 << 7) - #define GLOBAL_INTERRUPT_DISABLE SREG &= ~(1 << 7) + // --- Main Bus Interface (CD-ROM Controller) --- + // Define the main pins as inputs + #define PIN_DATA_INPUT DDRB &= ~(1 << DDB4) // DATA line (PB4) + #define PIN_WFCK_INPUT DDRB &= ~(1 << DDB5) // WFCK / GATE line (PB5) + #define PIN_SQCK_INPUT DDRD &= ~(1 << DDD7) // SQCK Clock (PD7) + #define PIN_SUBQ_INPUT DDRE &= ~(1 << DDE6) // SUBQ Data (PE6) - // Handling the main pins - - // Main pins - #define PIN_DATA_INPUT DDRB &= ~(1 << DDB4) - #define PIN_WFCK_INPUT DDRB &= ~(1 << DDB5) - #define PIN_SQCK_INPUT DDRD &= ~(1 << DDD7) - #define PIN_SUBQ_INPUT DDRE &= ~(1 << DDE6) - + // Configure lines as outputs (for injection/override) #define PIN_DATA_OUTPUT DDRB |= (1 << DDB4) #define PIN_WFCK_OUTPUT DDRB |= (1 << DDB5) - // Define pull-ups and set high at the main pin - #define PIN_DATA_SET PORTB |= (1 << PB4) - // Define pull-ups set down at the main pin - #define PIN_DATA_CLEAR PORTB &= ~(1 << PB4) - #define PIN_WFCK_CLEAR PORTB &= ~(1 << PB5) - // Read the main pins - #define PIN_SQCK_READ (PIND & (1 << PIND7)) - #define PIN_SUBQ_READ (PINE & (1 << PINE6)) - #define PIN_WFCK_READ (PINB & (1 << PINB5)) + // Bus line state control (Set High / Clear Low) + #define PIN_DATA_SET PORTB |= (1 << PB4) // Enable pull-up or drive HIGH + #define PIN_DATA_CLEAR PORTB &= ~(1 << PB4) // Drive line LOW + #define PIN_WFCK_CLEAR PORTB &= ~(1 << PB5) // Drive line LOW - // Handling and use of the LED pin + // Direct Register Reading (High-speed polling) + #define PIN_SQCK_READ (PIND & (1 << PIND7)) + #define PIN_SUBQ_READ (PINE & (1 << PINE6)) + #define PIN_WFCK_READ (PINB & (1 << PINB5)) + + // --- Status Indication (LED) --- #ifdef LED_RUN - #define PIN_LED_OUTPUT DDRB |= (1 << DDB6) - #define PIN_LED_ON PORTB |= (1 << PB6) - #define PIN_LED_OFF PORTB &= ~(1 << PB6) + #define PIN_LED_OUTPUT DDRB |= (1 << DDB6) // LED on PB6 + #define PIN_LED_ON PORTB |= (1 << PB6) + #define PIN_LED_OFF PORTB &= ~(1 << PB6) #endif - // Handling the BIOS patch - #if defined(SCPH_102) || defined(SCPH_102_legacy) || defined(SCPH_100) || defined(SCPH_7000_9000) || defined(SCPH_5500) || defined(SCPH_3500_5000) || defined(SCPH_3000) || defined(SCPH_1000) - // Pins input - #define PIN_AX_INPUT DDRD &= ~(1 << DDD1) - #define PIN_AY_INPUT DDRD &= ~(1 << DDD0) - #define PIN_DX_INPUT DDRD &= ~(1 << DDD4) - // Pin output - #define PIN_DX_OUTPUT DDRD |= (1 << DDD4) - // Define pull-ups set high - #define PIN_DX_SET PORTD |= (1 << PD4) - // Define pull-ups set down - #define PIN_DX_CLEAR PORTD &= ~(1 << PD4) - // Read pins for BIOS patch - #define PIN_AX_READ (PIND & (1 << PIND1)) - #define PIN_AY_READ (PIND & (1 << PIND0)) - // Handling and reading the switch pin for patch BIOS - #ifdef PATCH_SWITCH - #define PIN_SWITCH_INPUT DDRC &= ~(1 << DDC6) - #define PIN_SWITCH_SET PORTC |= (1 << PC6) - #define PIN_SWITCH_READ (PINC & (1 << PINC6)) + // --- BIOS Patching Configuration (32U4 Mapping) --- + #if defined(SCPH_102) || defined(SCPH_102_legacy) || defined(SCPH_100) || \ + defined(SCPH_7000_9000) || defined(SCPH_5500) || defined(SCPH_3500_5000) || \ + defined(SCPH_3000) || defined(SCPH_1000) + + // Address (AX / AY) and Data (DX) lines for BIOS override + #define PIN_AX_INPUT DDRD &= ~(1 << DDD1) // AX on PD1 (INT1) + #define PIN_AY_INPUT DDRD &= ~(1 << DDD0) // AY on PD0 (INT0) + #define PIN_DX_INPUT DDRD &= ~(1 << DDD4) // DX on PD4 + + #define PIN_DX_OUTPUT DDRD |= (1 << DDD4) + #define PIN_DX_SET PORTD |= (1 << PD4) + #define PIN_DX_CLEAR PORTD &= ~(1 << PD4) + + // Blocking wait macros for synchronization + #define WAIT_AX_RISING (!(PIND & (1 << PIND1))) // Wait for pulse start + #define WAIT_AX_FALLING (PIND & (1 << PIND1)) // Wait for pulse end + #define PIN_AX_READ (PIND & (1 << PIND1)) + + // Hardware Interrupt (INT1) for AX pulse counting + #define PIN_AX_INTERRUPT_ENABLE EIMSK |= (1 << INT1) + #define PIN_AX_INTERRUPT_DISABLE EIMSK &= ~(1 << INT1) + #define PIN_AX_INTERRUPT_RISING EICRA |= (1 << ISC11) | (1 << ISC10) + #define PIN_AX_INTERRUPT_VECTOR INT1_vect + + // Secondary Address line (AY) for multi-stage patching (INT0) + #if defined(SCPH_3000) || defined(SCPH_1000) + #define PIN_AY_READ (PIND & (1 << PIND0)) + #define WAIT_AY_RISING (!(PIND & (1 << PIND0))) + #define WAIT_AY_FALLING (PIND & (1 << PIND0)) + + #define PIN_AY_INTERRUPT_ENABLE EIMSK |= (1 << INT0) + #define PIN_AY_INTERRUPT_DISABLE EIMSK &= ~(1 << INT0) + #define PIN_AY_INTERRUPT_RISING EICRA |= (1 << ISC01) | (1 << ISC00) + #define PIN_AY_INTERRUPT_VECTOR INT0_vect #endif - // BIOS timer clear - #define TIMER_TIFR_CLEAR TIFR0 |= (1 << OCF0A) - - // Handling the external interrupt - #define PIN_AX_INTERRUPT_ENABLE EIMSK |= (1 << INT1) - #define PIN_AY_INTERRUPT_ENABLE EIMSK |= (1 << INT0) - - #define PIN_AX_INTERRUPT_DISABLE EIMSK &= ~(1 << INT1) - #define PIN_AY_INTERRUPT_DISABLE EIMSK &= ~(1 << INT0) - - #define PIN_AX_INTERRUPT_RISING EICRA |= (1 << ISC11) | (1 << ISC10) - #define PIN_AY_INTERRUPT_RISING EICRA |= (1 << ISC01) | (1 << ISC00) - - #define PIN_AX_INTERRUPT_FALLING (EICRA = (EICRA & ~(1 << ISC10)) | (1 << ISC11)) - #define PIN_AY_INTERRUPT_FALLING (EICRA = (EICRA & ~(1 << ISC00)) | (1 << ISC01)) - - #define PIN_AX_INTERRUPT_VECTOR INT1_vect - #define PIN_AY_INTERRUPT_VECTOR INT0_vect - + // Hardware Bypass Switch (On-the-fly deactivation) + #ifdef (SCPH_7000) + #define PIN_SWITCH_INPUT DDRC &= ~(1 << DDC6) // Bypass on PC6 + #define PIN_SWITCH_SET PORTC |= (1 << PC6) // Enable pull-up + #define PIN_SWITCH_READ (PINC & (1 << PINC6)) + #endif #endif - #endif + // // Globale interrupt seting + // #define GLOBAL_INTERRUPT_ENABLE SREG |= (1 << 7) + // #define GLOBAL_INTERRUPT_DISABLE SREG &= ~(1 << 7) + + + + // // Handling the main pins + + // // Main pins + // #define PIN_DATA_INPUT DDRB &= ~(1 << DDB4) + // #define PIN_WFCK_INPUT DDRB &= ~(1 << DDB5) + // #define PIN_SQCK_INPUT DDRD &= ~(1 << DDD7) + // #define PIN_SUBQ_INPUT DDRE &= ~(1 << DDE6) + + // #define PIN_DATA_OUTPUT DDRB |= (1 << DDB4) + // #define PIN_WFCK_OUTPUT DDRB |= (1 << DDB5) + + // // Define pull-ups and set high at the main pin + // #define PIN_DATA_SET PORTB |= (1 << PB4) + // // Define pull-ups set down at the main pin + // #define PIN_DATA_CLEAR PORTB &= ~(1 << PB4) + // #define PIN_WFCK_CLEAR PORTB &= ~(1 << PB5) + // // Read the main pins + // #define PIN_SQCK_READ (PIND & (1 << PIND7)) + // #define PIN_SUBQ_READ (PINE & (1 << PINE6)) + // #define PIN_WFCK_READ (PINB & (1 << PINB5)) + + // // Handling and use of the LED pin + // #ifdef LED_RUN + // #define PIN_LED_OUTPUT DDRB |= (1 << DDB6) + // #define PIN_LED_ON PORTB |= (1 << PB6) + // #define PIN_LED_OFF PORTB &= ~(1 << PB6) + // #endif + + // // Handling the BIOS patch + // #if defined(SCPH_102) || defined(SCPH_102_legacy) || defined(SCPH_100) || defined(SCPH_7000_9000) || defined(SCPH_5500) || defined(SCPH_3500_5000) || defined(SCPH_3000) || defined(SCPH_1000) + // // Pins input + // #define PIN_AX_INPUT DDRD &= ~(1 << DDD1) + // #define PIN_AY_INPUT DDRD &= ~(1 << DDD0) + // #define PIN_DX_INPUT DDRD &= ~(1 << DDD4) + // // Pin output + // #define PIN_DX_OUTPUT DDRD |= (1 << DDD4) + // // Define pull-ups set high + // #define PIN_DX_SET PORTD |= (1 << PD4) + // // Define pull-ups set down + // #define PIN_DX_CLEAR PORTD &= ~(1 << PD4) + // // Read pins for BIOS patch + // #define PIN_AX_READ (PIND & (1 << PIND1)) + // #define PIN_AY_READ (PIND & (1 << PIND0)) + // // Handling and reading the switch pin for patch BIOS + // #ifdef PATCH_SWITCH + // #define PIN_SWITCH_INPUT DDRC &= ~(1 << DDC6) + // #define PIN_SWITCH_SET PORTC |= (1 << PC6) + // #define PIN_SWITCH_READ (PINC & (1 << PINC6)) + // #endif + + // // BIOS timer clear + // #define TIMER_TIFR_CLEAR TIFR0 |= (1 << OCF0A) + + // // Handling the external interrupt + // #define PIN_AX_INTERRUPT_ENABLE EIMSK |= (1 << INT1) + // #define PIN_AY_INTERRUPT_ENABLE EIMSK |= (1 << INT0) + + // #define PIN_AX_INTERRUPT_DISABLE EIMSK &= ~(1 << INT1) + // #define PIN_AY_INTERRUPT_DISABLE EIMSK &= ~(1 << INT0) + + // #define PIN_AX_INTERRUPT_RISING EICRA |= (1 << ISC11) | (1 << ISC10) + // #define PIN_AY_INTERRUPT_RISING EICRA |= (1 << ISC01) | (1 << ISC00) + + // #define PIN_AX_INTERRUPT_FALLING (EICRA = (EICRA & ~(1 << ISC10)) | (1 << ISC11)) + // #define PIN_AY_INTERRUPT_FALLING (EICRA = (EICRA & ~(1 << ISC00)) | (1 << ISC01)) + + // #define PIN_AX_INTERRUPT_VECTOR INT1_vect + // #define PIN_AY_INTERRUPT_VECTOR INT0_vect + + // #endif + + //#endif + #ifdef ATtiny85_45_25 -static inline void optimizePeripherals(void) { - // --- Analog Modules Shutdown (ATtiny Specific) --- - // 1. Disable the ADC module - ADCSRA &= ~(1 << ADEN); - // 2. Disable the Analog Comparator - ACSR |= (1 << ACD); + static inline void OptimizePeripherals(void) { + // 1. Global Interrupt Disable during reconfiguration + cli(); - // --- Power Reduction Register (Hard Clock Shut Off) --- - // On ATtiny85, PRR controls Timer 0, Timer 1, USI, and ADC. - // We stop the ADC and Timer 0 clocks. - PRR |= (1 << PRADC) | (1 << PRTIM0); + // 2. Analog Modules Shutdown + ADCSRA &= ~(1 << ADEN); // Disable ADC + ACSR |= (1 << ACD); // Disable Analog Comparator - // --- Timer 0 Specific Shutdown --- - TCCR0B = 0; - TIMSK = 0; // On ATtiny85, it's TIMSK (not TIMSK0) -} + // 3. Digital Input Buffer Disable (DIDR0) + // Disconnects digital buffers on PB0-PB5 to prevent leakage + DIDR0 = 0x3F; - #define DF_CPU 8000000L - #define TIMER_TCNT_CLEAR TCNT0 = 0x00; - #define SET_OCROA_DIV OCR0A = 79; //OCR0A – Output Compare Register A,100KHz - #define SET_TIMER_TCCROA TCCR0A |= (1 << WGM01); - #define SET_TIMER_TCCROB TCCR0B |= (1 << CS00); - #define CTC_TIMER_VECTOR TIMER0_COMPA_vect + // 4. Power Reduction Register (PRR) + // Shuts down clocks to ADC and Timer 0. + // We KEEP USI or Timer 1 if required for specific logic. + PRR |= (1 << PRADC) | (1 << PRTIM0); + // 5. Timer 0 Specific Shutdown (Hardware Redundancy) + TCCR0B = 0; + TIMSK &= ~(1 << OCIE0A); // Disable Timer 0 interrupts + } #include @@ -514,59 +603,109 @@ static inline void optimizePeripherals(void) { #include #include - // Globale interrupt seting - #define GLOBAL_INTERRUPT_ENABLE SREG |= (1 << 7) - #define GLOBAL_INTERRUPT_DISABLE SREG &= ~(1 << 7) - // Handling the main pins + // --- Main Bus Interface (CD-ROM Controller) --- - // Main pins input - #define PIN_DATA_INPUT DDRB &= ~(1 << DDB2) - #define PIN_WFCK_INPUT DDRB &= ~(1 << DDB4) - #define PIN_SQCK_INPUT DDRB &= ~(1 << DDB0) - #define PIN_SUBQ_INPUT DDRB &= ~(1 << DDB1) + // Define the main pins as inputs + #define PIN_DATA_INPUT DDRB &= ~(1 << DDB2) // DATA line (PB2) + #define PIN_WFCK_INPUT DDRB &= ~(1 << DDB4) // WFCK / GATE line (PB4) + #define PIN_SQCK_INPUT DDRB &= ~(1 << DDB0) // SQCK Clock (PB0) + #define PIN_SUBQ_INPUT DDRB &= ~(1 << DDB1) // SUBQ Data (PB1) - // Main pin output + // Configure lines as outputs (for injection/override) #define PIN_DATA_OUTPUT DDRB |= (1 << DDB2) #define PIN_WFCK_OUTPUT DDRB |= (1 << DDB4) - // Define pull-ups and set high at the main pin - #define PIN_DATA_SET PORTB |= (1 << PB2) + // Bus line state control (Set High / Clear Low) + #define PIN_DATA_SET PORTB |= (1 << PB2) // Enable pull-up or drive HIGH + #define PIN_DATA_CLEAR PORTB &= ~(1 << PB2) // Drive line LOW + #define PIN_WFCK_CLEAR PORTB &= ~(1 << PB4) // Drive line LOW - // Define pull-ups set down at the main pin - #define PIN_DATA_CLEAR PORTB &= ~(1 << PB2) - #define PIN_WFCK_CLEAR PORTB &= ~(1 << PB4) + // Direct Register Reading (High-speed polling) + #define PIN_SQCK_READ (PINB & (1 << PINB0)) + #define PIN_SUBQ_READ (PINB & (1 << PINB1)) + #define PIN_WFCK_READ (PINB & (1 << PINB4)) - // Read the main pins - #define PIN_SQCK_READ (PINB & (1 << PINB0)) - #define PIN_SUBQ_READ (PINB & (1 << PINB1)) - #define PIN_WFCK_READ (PINB & (1 << PINB4)) - - #define TIMER_INTERRUPT_ENABLE TIMSK |= (1 << OCIE0A) + // Timer Interrupt Management + #define TIMER_INTERRUPT_ENABLE TIMSK |= (1 << OCIE0A) #define TIMER_INTERRUPT_DISABLE TIMSK &= ~(1 << OCIE0A) - // Handling and use of the LED pin + // --- Status Indication (LED) --- #ifdef LED_RUN - #define PIN_LED_OUTPUT DDRB |= (1 << DDB3) - #define PIN_LED_ON PORTB |= (1 << PB3) - #define PIN_LED_OFF PORTB &= ~(1 << PB3) - #endif - - #if defined(SCPH_1000) || defined(SCPH_3000) || defined(SCPH_3500_5000) || defined(SCPH_5500) || defined(SCPH_7000_9000) || defined(SCPH_100) || defined(SCPH_102_legacy) || defined(SCPH_102) - #error "ATtiny85_45_25 Not compatible with BIOS patch" + #define PIN_LED_OUTPUT DDRB |= (1 << DDB3) // LED on PB3 + #define PIN_LED_ON PORTB |= (1 << PB3) + #define PIN_LED_OFF PORTB &= ~(1 << PB3) #endif + // --- Debug Serial (Software Serial) --- #if defined(PSNEE_DEBUG_SERIAL_MONITOR) #include - SoftwareSerial mySerial(-1, 3); // RX, TX. (RX -1 = off) + // SoftwareSerial(RX, TX): RX set to -1 (disabled), TX on PB3 + // Note: Resource conflict if LED_RUN is also on PB3 + SoftwareSerial mySerial(-1, 3); #define PIN_TXD_OUTPUT DDRB |= (1 << DDB3) - // #define DEBUG_PRINT(x) mySerial.print(x) - // #define DEBUG_PRINTHEX(x) mySerial.print(x, HEX) - // #define DEBUG_PRINTLN(x) mySerial.println(x) - // #define DEBUG_FLUSH mySerial.flush() #endif + // --- Safety Check: BIOS Patch Compatibility --- + #if defined(SCPH_1000) || \ + defined(SCPH_3000) || \ + defined(SCPH_3500_5000) || \ + defined(SCPH_5500) || \ + defined(SCPH_7000_9000) || \ + defined(SCPH_100) || \ + defined(SCPH_102) + #error "ATtiny85/45/25 architecture is not compatible with the BIOS patch feature." + #endif #endif +// // Handling the main pins + +// // Main pins input +// #define PIN_DATA_INPUT DDRB &= ~(1 << DDB2) +// #define PIN_WFCK_INPUT DDRB &= ~(1 << DDB4) +// #define PIN_SQCK_INPUT DDRB &= ~(1 << DDB0) +// #define PIN_SUBQ_INPUT DDRB &= ~(1 << DDB1) + +// // Main pin output +// #define PIN_DATA_OUTPUT DDRB |= (1 << DDB2) +// #define PIN_WFCK_OUTPUT DDRB |= (1 << DDB4) + +// // Define pull-ups and set high at the main pin +// #define PIN_DATA_SET PORTB |= (1 << PB2) + +// // Define pull-ups set down at the main pin +// #define PIN_DATA_CLEAR PORTB &= ~(1 << PB2) +// #define PIN_WFCK_CLEAR PORTB &= ~(1 << PB4) + +// // Read the main pins +// #define PIN_SQCK_READ (PINB & (1 << PINB0)) +// #define PIN_SUBQ_READ (PINB & (1 << PINB1)) +// #define PIN_WFCK_READ (PINB & (1 << PINB4)) + +// #define TIMER_INTERRUPT_ENABLE TIMSK |= (1 << OCIE0A) +// #define TIMER_INTERRUPT_DISABLE TIMSK &= ~(1 << OCIE0A) + +// // Handling and use of the LED pin +// #ifdef LED_RUN +// #define PIN_LED_OUTPUT DDRB |= (1 << DDB3) +// #define PIN_LED_ON PORTB |= (1 << PB3) +// #define PIN_LED_OFF PORTB &= ~(1 << PB3) +// #endif + +// #if defined(SCPH_1000) || defined(SCPH_3000) || defined(SCPH_3500_5000) || defined(SCPH_5500) || defined(SCPH_7000_9000) || defined(SCPH_100) || defined(SCPH_102_legacy) || defined(SCPH_102) +// #error "ATtiny85_45_25 Not compatible with BIOS patch" +// #endif + +// #if defined(PSNEE_DEBUG_SERIAL_MONITOR) +// #include +// SoftwareSerial mySerial(-1, 3); // RX, TX. (RX -1 = off) +// #define PIN_TXD_OUTPUT DDRB |= (1 << DDB3) +// // #define DEBUG_PRINT(x) mySerial.print(x) +// // #define DEBUG_PRINTHEX(x) mySerial.print(x, HEX) +// // #define DEBUG_PRINTLN(x) mySerial.println(x) +// // #define DEBUG_FLUSH mySerial.flush() +// #endif + +// #endif // ***************************************************************************************************************** // WARNING: diff --git a/PSNee/PSNee.ino b/PSNee/PSNee.ino index bd85481..9bbec61 100644 --- a/PSNee/PSNee.ino +++ b/PSNee/PSNee.ino @@ -47,8 +47,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX //#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_5000_5500 // DX - D0 | AX - A15 | | 3.0j - CRC FF3EEB8C, 2.2j - CRC 24FC7E17 -//#define SCPH_3500 // DX - D0 | AX - A15 | AX - A15 | 2.1j - CRC BC190209 +//#define SCPH_3500_5500 // DX - D0 | AX - A16 | AX - A15 | 3.0j - CRC FF3EEB8C, 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 @@ -130,7 +129,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX volatile uint8_t wfck_mode = 0; // Variables de contrôle globales -// Global buffer to store the 12-byte Sub-Q channel data +// Global buffer to store the 12-byte SUBQ channel data uint8_t subqBuffer[12]; uint8_t hysteresis = 0; @@ -145,58 +144,67 @@ uint8_t hysteresis = 0; /*---------------------------------------------------------------------- - Function: board_detection + * 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) + * + * + * 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: + * - Initial/Protection Phase: ~7.3 kHz. + * - Standard Data Reading: ~14.6 kHz. + * + *-----------------------------------------------------------------------*/ - This function distinguishes motherboard generations by detecting - the nature of the WFCK signal: - - WFCK: __----------------------- // CONTINUOUS (PU-7 .. PU-20)(GATE) - - WFCK: __-_-_-_-_-_-_-_-_-_-_-_- // FREQUENCY (PU-22 or newer) - - Traditionally, the WFCK signal was called GATE. This is because, on early models, - modchips acted like a gate that would open to pull the signal down - at the exact moment the region code was being passed (which is still the case today). - - During the initialization and region protection zone reading phases, - the WFCK clock frequency is approximately 7.3 kHz. - During normal data reading, the frequency shifts to 14.6 kHz. - ------------------------------------------------------------------------*/ - - -void board_detection() { - // Default to static signal (PU-7 to PU-20) +void BoardBetection() { + // Default state: 0 (Static/GATE mode for PU-7 to PU-20) wfck_mode = 0; - /* - 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. - */ + /** + * INITIAL STABILIZATION DELAY (300ms) + * - PU-7/20: Voltage ramp-up takes ~54ms before stabilizing at a logic HIGH. + * - PU-22+: Clock oscillation (~7.3kHz) typically begins after ~297ms. + * This delay ensures all power-up transients and initial noise are bypassed. + */ _delay_ms(300); - // Sampling window to detect the oscillating signal + // Define a sampling window to capture potential oscillations 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. - */ + /** + * DETECTION LOGIC: + * On legacy boards (PU-7/20), WFCK is now a steady HIGH. + * Detecting a LOW state indicates a potential clock cycle from a newer board. + */ if (!PIN_WFCK_READ) { - // Small debounce delay to filter out micro-glitches or remaining noise + // Software debounce to filter out micro-glitches or parasitic noise uint8_t debounce = 100; while (--debounce); - /* - 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. - */ + /** + * VERIFICATION: + * If the signal remains LOW after the debounce, we confirm a genuine + * clock oscillation (WFCK). Legacy boards never transition to LOW + * once stabilized at HIGH during this phase. + */ if (!PIN_WFCK_READ) { - wfck_mode = 1; // Target: PU-22 or newer + wfck_mode = 1; // Target confirmed: PU-22 or newer (Frequency mode) return; } } @@ -207,14 +215,18 @@ void board_detection() { #endif } -//****************************************************************************************************************** -// Reads a complete 12-byte SUBQ transmission from the CD drive. -// Uses clock-edge synchronization and includes a safety timeout for malformatted streams. -//****************************************************************************************************************** +/****************************************************************************************************************** + * FUNCTION : CaptureSUBQ + * + * DESCRIPTION : + * Captures a complete 12-byte SUBQ frame from the CD controller. + * Synchronizes with the SQCK (Sub-Q Clock) pin to shift in serial data from + * the SUBQ pin. This implementation follows the standard PlayStation CDIC + * protocol (Synchronous Serial, LSB first). + ******************************************************************************************************************/ -void captureSubQ(void) { - // Total bytes to read from the CD-ROM subcode channel - uint8_t bytesRemaining = 12; +void CaptureSUBQ(void) { + uint8_t bytesRemaining = 12; // Total frame size for a complete SUBQ uint8_t* bufferPtr = subqBuffer; do { @@ -222,12 +234,19 @@ void captureSubQ(void) { 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. + /** + * 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. + */ while (PIN_SQCK_READ); // Wait for falling edge while (!PIN_SQCK_READ); // Wait for rising edge - // PHASE 2: Shift bit into the byte (LSB first) + /** + * 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). + */ currentByte >>= 1; if (PIN_SUBQ_READ) { currentByte |= 0x80; @@ -235,39 +254,46 @@ void captureSubQ(void) { } // Store reconstructed byte and advance pointer *bufferPtr++ = currentByte; - } while (--bytesRemaining); // Faster than (bytesRemaining < 12) + + } while (--bytesRemaining); // Efficient countdown for AVR binary size #if defined(PSNEE_DEBUG_SERIAL_MONITOR) - logSubQ(subqBuffer); + LogSUBQ(subqBuffer); #endif } -/************************************************************************************** - * Processes sector data for the SCPH-5903 (Dual-interface PS1) to differentiate - * between PlayStation games and Video CDs (VCD). +/****************************************************************************************** + * FUNCTION : Filter_SUBQ_Samples() [SCPH_5903 Dual-Interface Variant] * - * 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. - -**************************************************************************************/ + * DESCRIPTION : + * Parses and filters the raw serial data stream from the SUBQ pin specifically + * for the SCPH-5903 model to differentiate between Game Discs and Video CDs (VCD). + * + * 1. VCD EXCLUSION: Specifically filters out VCD Lead-In patterns (sub-mode 0x02) + * to prevent incorrect region injection on non-game media. + * 2. SUBQ HIT COUNTING: Increments 'hysteresis' for valid PlayStation TOC (A0-A2) + * markers or active game tracking. + * 3. SIGNAL DECAY: Decrements the counter when SUBQ samples match VCD patterns + * or unknown data, ensuring stable disc identification. + * + * INPUT : isDataSector (bool) - Filtered flag based on raw sector control bits. + ******************************************************************************************/ #ifdef SCPH_5903 - void processLogic(uint8_t isDataSector) { + void FilterSUBQSamples(uint8_t isDataSector) { uint8_t currentHysteresis = hysteresis; - // Fast filtering: most sectors fail here by checking sync markers (index 1 and 6) + // --- 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]; - /* - 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. - */ + /** + * 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)) ) { @@ -276,46 +302,45 @@ void captureSubQ(void) { } } - /* - DECREMENT CONDITION: - No match found or VCD Lead-In detected. Slowly decrease confidence level. - */ + // --- 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; } } #else + /****************************************************************************************** - * Heuristic logic for standard PlayStation hardware (Non-VCD models). + * FUNCTION : Filter_SUBQ_Samples() * - * 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. - -******************************************************************************************/ + * DESCRIPTION : + * Parses and filters the raw serial data stream from the SUBQ pin. + * Increments a hit counter (hysteresis) when specific patterns are identified + * in the SUBQ stream, confirming the laser is reading the region-check area. + * + * 1. RAW BUS FILTERING: Validates SUBQ framing by checking sync markers (index 1 & 6). + * 2. PATTERN MATCHING: Detects Lead-In TOC (A0-A2) or Track 01 at the spiral start. + * 3. SIGNAL DECAY: Decrements the counter if the current SUBQ sample does not + * match expected PlayStation protection patterns. + * + * INPUT : isDataSector (bool) - Filtered flag based on raw sector control bits. + ******************************************************************************************/ -void processLogic(uint8_t isDataSector) { +void FilterSUBQSamples(uint8_t isDataSector) { uint8_t currentHysteresis = hysteresis; - // Fast filtering: most sectors fail here by checking sync markers (index 1 and 6) + // --- 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]; - /* - 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. - */ + /* + * 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)) ) { @@ -324,10 +349,8 @@ void processLogic(uint8_t isDataSector) { } } - /* - DECREMENT CONDITION: - No valid match found. Slowly decrease confidence level. - */ + // --- STEP 2: Signal Decay / Missed Hits --- + // Reduce the hit counter if the current SUBQ sample fails validation. if (currentHysteresis > 0) { hysteresis = currentHysteresis - 1; } @@ -349,7 +372,7 @@ void processLogic(uint8_t isDataSector) { * handles both speeds as it syncs directly to the signal edges. *********************************************************************************************/ -void performInjectionSequence(uint8_t injectSCEx) { +void PerformInjectionSequence(uint8_t injectSCEx) { /* Security strings (44-bit SCEx) for the three main regions: 0: NTSC-J (SCEI - Sony Computer Entertainment Inc.) @@ -454,40 +477,30 @@ void performInjectionSequence(uint8_t injectSCEx) { #if defined(PSNEE_DEBUG_SERIAL_MONITOR) - Debug_Inject(); + DebugInject(); #endif } void Init() { + // --- Hardware Power & Peripheral Optimization --- #if defined(ATmega328_168) - optimizePeripherals(); + OptimizePeripherals(); #endif #ifdef LED_RUN PIN_LED_OUTPUT; #endif + // --- Critical Boot Patching --- #ifdef BIOS_PATCH - //uint8_t skipPatch = 0; - -// #ifdef SCPH_7000 -// // Check hardware switch for SCPH-7000 models -// PIN_SWITCH_INPUT; -// PIN_SWITCH_SET; -// if (PIN_SWITCH_READ == 0){ -// skipPatch =1; // Disable patching if switch is triggered -// } -// #endif // #ifdef LED_RUN // PIN_LED_ON; // #endif - // Execute BIOS patching unless bypassed by switch - // if (skipPatch == 0) { - Bios_Patching(); - // } + // Execute BIOS patching + Bios_Patching(); // #ifdef LED_RUN // PIN_LED_OFF; @@ -497,6 +510,7 @@ void Init() { PIN_SQCK_INPUT; PIN_SUBQ_INPUT; + // --- Debug Interface Setup --- #if defined(PSNEE_DEBUG_SERIAL_MONITOR) && defined(ATtiny85_45_25) //pinMode(debugtx, OUTPUT); // software serial tx pin mySerial.begin(115200); // 13,82 bytes in 12ms, max for softwareserial. (expected data: ~13 bytes / 12ms) // update: this is actually quicker @@ -504,38 +518,38 @@ 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(); -} + // --- Console Analysis --- + // Identify board revision (PU-7 to PU-22+) to set correct injection timings + BoardBetection(); + } int main() { Init(); - while (1) { - // Small delay to prevent re-reading the tail end of the same SUBQ packet + // 1. Timing Sync: Prevent reading the tail end of the previous SUBQ packet _delay_ms(1); - //Capture the 12-byte Sub-Q channel data into subqBuffer - captureSubQ(); + // 2. Data Acquisition: Capture 12-byte SUBQ channel stream into buffer + CaptureSUBQ(); - // Optimized Sector Filtering: + // 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 isDataSector = ((scbuf[0] & 0xD0) == 0x40); - uint8_t isDataSector = ((subqBuffer[0] & 0xD0) == 0x40); // Execute selected logic - processLogic(isDataSector); + FilterSUBQSamples(isDataSector); - //Trigger SCEx injection once the confidence threshold is reached + // 5. Execution: Trigger SCEx injection once confidence (hysteresis) is reached if (hysteresis >= HYSTERESIS_MAX) { - performInjectionSequence(INJECT_SCEx); + PerformInjectionSequence(INJECT_SCEx); } } + return 0; } diff --git a/PSNee/settings.h b/PSNee/settings.h index 52993a7..273dc96 100644 --- a/PSNee/settings.h +++ b/PSNee/settings.h @@ -1,27 +1,36 @@ #pragma once -/* - The _delay_us function uses loops to generate an approximate delay for the specified number of microseconds. - It calculates the number of clock cycles required to achieve the requested delay and loops the corresponding number of times. - - The temporal precision of _delay_us depends on the microcontroller's clock frequency (F_CPU). - For the ATmega328 operating at a typical frequency of 16 MHz, here are some details on the precision. - - -*/ /*------------------------------------------------------------------------------------------------ - Specific parameter section for BIOS patches -------------------------------------------------------------------------------------------------*/ + * BIOS PATCH PARAMETERS EXPLANATION + * + * 1. SILENCE_THRESHOLD: + * Defines the size of a polling block where the AX pin must remain LOW. + * It serves as a "bus address call detector" for specific idle windows. + * + * 2. CONFIRM_COUNTER_TARGET: + * Defines the exact number of SILENCE_THRESHOLD blocks that must be validated + * consecutively before arming the hardware interrupt (ISR). + * + * 3. PULSE_COUNT: + * The starting value for the 'impulse' down-counter. It counts physical + * rising edges on the AX pin until it reaches zero to trigger the injection. + * + * 4. BIT_OFFSET_CYCLES (Inside ISR): + * The sub-microsecond delay (CPU cycles) applied after the final pulse to + * precisely align the DX injection with the target bit in the memory cycle. + * + * 5. OVERRIDE_CYCLES (Inside ISR): + * The duration for which the DX pin is forced to OUTPUT mode to overwrite + * the original BIOS data with the custom state. + ------------------------------------------------------------------------------------------------*/ + // tested with an Atmega328P - - // ------ SCPH 100 / 102 ------ #if defined(SCPH_100) || \ defined(SCPH_102) - #define BIOS_PATCH #define SILENCE_THRESHOLD 1500 #define CONFIRM_COUNTER_TARGET 8 @@ -53,8 +62,8 @@ #endif -// // ----- SCPH 5000 / 5500 ----- -#ifdef SCPH_5000_5500 +// // ----- SCPH 3500 / 5000 / 5500 ----- +#ifdef SCPH_3500_5500 #define BIOS_PATCH #define SILENCE_THRESHOLD 35600 #define CONFIRM_COUNTER_TARGET 1 @@ -63,30 +72,6 @@ #define OVERRIDE_CYCLES 3 #endif - -// // // -------- SCPH 5000 -------- -// #ifdef SCPH_5000 -// #define BIOS_PATCH_3 -// #define INTERRUPT_RISING -// #define SILENCE_THRESHOLD 35000 -// #define CONFIRM_COUNTER_TARGET 1 -// #define PULSE_COUNT 84 -// #define BIT_OFFSET_CYCLES 60 -// #define OVERRIDE_CYCLES 3 -// #endif -// // #endif - -// // -------- SCPH 3500 -------- -#ifdef SCPH_3500 - #define BIOS_PATCH - #define SILENCE_THRESHOLD 34000 - #define CONFIRM_COUNTER_TARGET 1 - #define PULSE_COUNT 85 - #define BIT_OFFSET_CYCLES 44 - #define OVERRIDE_CYCLES 3 -#endif - - // // -------- SCPH 3000 -------- #ifdef SCPH_3000 #define BIOS_PATCH @@ -118,7 +103,7 @@ #define BIT_OFFSET_2_CYCLES 54 #define OVERRIDE_2_CYCLES 3 #endif -// #endif + /*------------------------------------------------------------------------------------------------ Region Settings Section ------------------------------------------------------------------------------------------------*/ @@ -126,7 +111,7 @@ #if defined(SCPH_100) || \ defined(SCPH_7500_9000) || \ defined(SCPH_7000) || \ - defined(SCPH_5000_5500) || \ + defined(SCPH_3500_5500) || \ defined(SCPH_3500) || \ defined(SCPH_3000) || \ defined(SCPH_1000) || \ @@ -156,39 +141,81 @@ serial debug section ------------------------------------------------------------------------------------------------*/ +/****************************************************************************************** + * FUNCTION : Debug_Log + * + * DESCRIPTION : + * Transmits hardware status and system configuration via the serial interface. + * Displays MCU frequency, WFCK detection mode, and active regional settings. + * + * - ATtiny: Uses a compact "Short-Code" format to minimize serial bus overhead. + * - Standard MCUs: Provides verbose, human-readable system diagnostics. + * + * INPUT : Wfck_mode (int) - The detected board generation (0: Static, 1: Frequency). + ******************************************************************************************/ #if defined(PSNEE_DEBUG_SERIAL_MONITOR) -void Debug_Log (int Wfck_mode){ //Information about the MCU, and old or late console mode. + void Debug_Log (int Wfck_mode){ -#if defined(ATtiny85_45_25) - mySerial.print("m "); mySerial.println(Wfck_mode); -#else - Serial.print(" MCU frequency: "); Serial.print(F_CPU); Serial.println(" Hz"); - //Serial.print(" lows: "); Serial.println(Lows); - Serial.print(" wfck_mode: "); Serial.println(Wfck_mode); - Serial.print(" region: "); Serial.print(region[0]); Serial.print(region[1]); Serial.println(region[2]); -#endif -} + #if defined(ATtiny85_45_25) + // --- COMPACT SYSTEM LOG (ATtiny) --- + // Minimalistic output to save CPU cycles and maintain timing precision. + mySerial.print("m "); + mySerial.println(Wfck_mode); // Mode indicator + #else + // --- VERBOSE DIAGNOSTICS (ATmega) --- + // Detailed system information for standard development and debugging. + Serial.print(" MCU frequency: "); + Serial.print(F_CPU); + Serial.println(" Hz"); -// 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) { + Serial.print(" wfck_mode: "); + Serial.println(Wfck_mode); // Board generation (0: Legacy, 1: Modern) + + Serial.print(" region: "); + Serial.print(region[0]); + Serial.print(region[1]); + Serial.println(region[2]); // Active injection string (e.g., SCEE) + #endif + } + +/****************************************************************************************** + * FUNCTION : LogSUBQ + * + * DESCRIPTION : + * Logs captured SUBQ frames to the serial interface. + * Timing is critical: the entire 12-byte frame must be processed and transmitted + * within the ~12ms window before the next SUBQ packet arrives. + * + * - ATtiny: Uses minimal formatting (no spaces) to stay within the timing budget. + * - Standard MCUs: Includes spaces for better readability. + * + * INPUT : dataBuffer (uint8_t*) - Pointer to the 12-byte SUBQ frame. + ******************************************************************************************/ + +void LogSUBQ(uint8_t *dataBuffer) { - // A bad sector read results in zeros (except for CRC). Skip logging if first 4 bytes are 0. + /** + * ERROR FILTERING: + * A failed sector read usually results in zeroed data (excluding CRC). + * Skip logging if the first 4 bytes are null to reduce bus traffic. + */ if (!(dataBuffer[0] == 0 && dataBuffer[1] == 0 && dataBuffer[2] == 0 && dataBuffer[3] == 0)) { #if defined(ATtiny85_45_25) - // Compact formatting for ATtiny to meet the 12ms timing constraint + // --- COMPACT FORMATTING (ATtiny) --- + // Minimal formatting to meet the strict 12ms timing constraint on slower MCUs. for (uint8_t i = 0; i < 12; i++) { if (dataBuffer[i] < 0x10) { - mySerial.print("0"); // Leading zero padding + mySerial.print("0"); // Leading zero padding for hex alignment } mySerial.print(dataBuffer[i], HEX); } mySerial.println(""); #else - // Standard formatting with spaces for more powerful MCUs + // --- STANDARD FORMATTING (ATmega) --- + // More descriptive output with space separation for easier debugging. for (uint8_t i = 0; i < 12; i++) { if (dataBuffer[i] < 0x10) { Serial.print("0"); // Leading zero padding @@ -202,66 +229,134 @@ void logSubQ(uint8_t *dataBuffer) { } } - -void Debug_Inject(){ // Confirmation of region code injection +/****************************************************************************************** + * FUNCTION : Debug_Inject + * + * DESCRIPTION : + * Provides real-time visual confirmation during SCEx region code injection. + * Used to verify that the hysteresis threshold has been met and the + * injection sequence is active. + * + * - ATtiny: Prints a single '!' to minimize CPU blocking during the critical + * injection timing window. + * - Standard MCUs: Prints a clear, verbose "INJECT !" message. + ******************************************************************************************/ +void DebugInject(){ #if defined(ATtiny85_45_25) - mySerial.print("!"); + // --- MINIMALIST NOTIFICATION (ATtiny) --- + mySerial.print("!"); + #else + // --- VERBOSE NOTIFICATION (ATmega) --- + // Standard visual feedback for debugging and monitoring. Serial.println(" INJECT ! "); #endif } #endif -/*------------------------------------------------------------------------------------------------ - Compilation message ------------------------------------------------------------------------------------------------*/ +/* + * ============================================================= + * COMPILATION CHECKS + * ============================================================= + */ -#if !defined(SCPH_xxx3) && \ - !defined(SCPH_102) && \ - !defined(SCPH_101) && \ - !defined(SCPH_100) && \ - !defined(SCPH_7500_9000) && \ - !defined(SCPH_7000) && \ - !defined(SCPH_5000_5500) && \ - !defined(SCPH_3500) && \ +// --- CONSOLE SELECTION CHECK --- +#if !defined(SCPH_1000) && \ !defined(SCPH_3000) && \ - !defined(SCPH_1000) && \ - !defined(SCPH_5903) && \ + !defined(SCPH_3500_5500) && \ + !defined(SCPH_7000) && \ + !defined(SCPH_7500_9000) && \ + !defined(SCPH_100) && \ + !defined(SCPH_101) && \ + !defined(SCPH_102) && \ !defined(SCPH_xxx1) && \ !defined(SCPH_xxx2) && \ + !defined(SCPH_xxx3) && \ + !defined(SCPH_5903) && \ !defined(SCPH_xxxx) - #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_5000_5500) ^ \ - defined(SCPH_3500) ^ \ - defined(SCPH_3000) ^ \ - 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." + #error "No console selected! Please uncomment your SCPH model." + +#elif (defined(SCPH_1000) + \ + defined(SCPH_3000) + \ + defined(SCPH_3500_5500) + \ + defined(SCPH_7000) + \ + defined(SCPH_7500_9000) + \ + defined(SCPH_100) + \ + defined(SCPH_101) + \ + defined(SCPH_102) + \ + defined(SCPH_xxx1) + \ + defined(SCPH_xxx2) + \ + defined(SCPH_xxx3) + \ + defined(SCPH_5903) + \ + defined(SCPH_xxxx) > 1) + #error "Multiple consoles selected! Please enable only one SCPH model." #endif +// --- MCU SELECTION CHECK --- #if !defined(ATmega328_168) && \ !defined(ATmega32U4_16U4) && \ !defined(ATtiny85_45_25) - #error "MCU not selected! Please choose one" -#elif !defined(ATmega328_168) ^ \ - defined(ATmega32U4_16U4 ) ^ \ - defined(ATtiny85_45_25) - #error "May be selected only one MCU" + #error "No MCU selected! Please choose one supported architecture." + +#elif (defined(ATmega328_168) + \ + defined(ATmega32U4_16U4) + \ + defined(ATtiny85_45_25) > 1) + #error "Multiple MCUs selected! Please enable only one architecture." #endif -#if defined(LED_RUN) && \ - defined(PSNEE_DEBUG_SERIAL_MONITOR) && \ - defined(ATtiny85_45_25) - #error"Compilation options LED_RUN and PSNEE_DEBUG_SERIAL_MONITOR are not simultaneously compatible with ATtiny85_45_25" +// --- RESOURCE CONFLICT CHECK (ATtiny) --- +#if defined(ATtiny85_45_25) && \ + defined(LED_RUN) && \ + defined(PSNEE_DEBUG_SERIAL_MONITOR) + #error "Resource conflict: LED_RUN and DEBUG_SERIAL are incompatible on ATtiny." +#endif + +// --- Console Model Info --- +#if defined(SCPH_1000) + #pragma message "Target Console: SCPH-1000 (NTSC-J)" +#elif defined(SCPH_3000) + #pragma message "Target Console: SCPH-3000 (NTSC-J)" +#elif defined(SCPH_3500_5500) + #pragma message "Target Console: SCPH-3500/5000/5500 (NTSC-J)" +#elif defined(SCPH_7000) + #pragma message "Target Console: SCPH-7000 (Internal Switch enabled)" +#elif defined(SCPH_7500_9000) + #pragma message "Target Console: SCPH-7500/9000 (NTSC-J)" +#elif defined(SCPH_100) + #pragma message "Target Console: SCPH-100 (NTSC-J)" +#elif defined(SCPH_101) + #pragma message "Target Console: SCPH-101 (NTSC-U/C)" +#elif defined(SCPH_102) + #pragma message "Target Console: SCPH-102 (PAL)" +#elif defined(SCPH_xxx1) + #pragma message "Target Console: Generic NTSC-U/C" +#elif defined(SCPH_xxx2) + #pragma message "Target Console: Generic PAL" +#elif defined(SCPH_xxx3) + #pragma message "Target Console: Generic NTSC-J" +#elif defined(SCPH_5903) + #pragma message "Target Console: SCPH-5903 (Video CD Dual-Interface)" +#elif defined(SCPH_xxxx) + #pragma message "Target Console: Universal Region Mode" +#endif + +// --- MCU Architecture Info --- +#if defined(ATmega328_168) + #pragma message "Microcontroller: ATmega328/168 (Arduino Nano/Uno)" +#elif defined(ATmega32U4_16U4) + #pragma message "Microcontroller: ATmega32U4/16U4 (Leonardo/Pro Micro)" +#elif defined(ATtiny85_45_25) + #pragma message "Microcontroller: ATtiny85/45/25" +#endif + +// --- Feature Status --- + +#ifdef PSNEE_DEBUG_SERIAL_MONITOR + #pragma message "Feature: Serial Debug Monitor ENABLED" +#endif + +#ifdef LED_RUN + #pragma message "Feature: Status LED (PB5) ENABLED" #endif