diff --git a/PSNee/BIOS_patching.h b/PSNee/BIOS_patching.h index 6b87ec2..c40e002 100644 --- a/PSNee/BIOS_patching.h +++ b/PSNee/BIOS_patching.h @@ -1,262 +1,337 @@ - #pragma once +/* + * ====================================================================================== + * FUNCTION : Bios_Patching() + * TARGET : Data Bus (DX) synchronized via Address Bus (AX / AY) + * + * OPERATIONAL LOGIC: + * Intercepts specific memory transactions by monitoring address lines (AX/AY). + * Uses hardware interrupts (ISR) for high-speed pulse counting and cycle-accurate + * 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 + * edge of the AX signal to establish a deterministic timing reference. + * + * 2. ADDRESS CALL DETECTION (PHASE 2): Scans the bus for specific address calls + * by validating consecutive polling blocks (SILENCE_THRESHOLD). + * + * 3. SILENCE CONFIRMATION (GATING): Counts the exact number of validated silence + * windows (CONFIRM_COUNTER_TARGET) to reach the correct pre-patching stage. + * + * 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). + * ====================================================================================== + */ + #ifdef BIOS_PATCH - uint8_t current_pulses = 0; - volatile uint8_t pulse_counter = 0; - volatile uint8_t patch_done = 0; - #ifdef TEST_BIOS +/** + * 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; // Down-counter for physical address pulses +volatile uint8_t patch = 0; // Synchronization flag (0: Idle, 1: AX Done, 2: AY Done) - void Bios_Patching() { - PIN_DX_INPUT; - //PIN_DX_LOW; +/** + * PHASE 3: Primary Interrupt Service Routine (AX) + * Triggered on rising edges to perform the real-time bus override. + */ +ISR(PIN_AX_INTERRUPT_VECTOR) { + if (--impulse == 0) { + // Precise bit-alignment delay within the memory cycle + __builtin_avr_delay_cycles(BIT_OFFSET_CYCLES); - - cli(); // Disable interrupts for timing integrity - - /* - * PHASE 1: Signal Stabilization & Alignment - * Synchronizes the MCU with the PS1 startup state (Cold Boot vs Reset). - */ - if (PIN_AX_READ != 0) { // Case: Power-on / Line high (---__-_-_) - while (PIN_AX_READ != 0); // Wait for falling edge - while (PIN_AX_READ == 0); // Sync on first clean rising edge - } else { // Case: Reset / Line low (_____-_-_) - while (PIN_AX_READ == 0); // Wait for rising edge + #ifdef PHASE_TWO_PATCH + PIN_DX_SET; // Pre-drive high if required by specific logic + #endif + + // DATA OVERDRIVE: Pull the DX bus to the custom state + PIN_DX_OUTPUT; + __builtin_avr_delay_cycles(OVERRIDE_CYCLES); + + #ifdef PHASE_TWO_PATCH + PIN_DX_CLEAR; + #endif + + // BUS RELEASE: Return DX to High-Z (Input) mode + PIN_DX_INPUT; + + PIN_AX_INTERRUPT_DISABLE; // Stop tracking AX pulses + PIN_LED_OFF; + patch = 1; // Signal Phase 3 completion } +} - /* - * PHASE 2: Address Bus Window Alignment - * Bypassing initial boot routines to reach one window with a - * known "idle gap" in the address bus activity, positioned - * immediately before the target memory-access cycle. - * BOOT_OFFSET: |----//----------| - * AX: ___-_-_//-_-_-________________-_-_ - */ - _delay_ms(BOOT_OFFSET); - PIN_LED_ON; +#ifdef PHASE_TWO_PATCH +/** + * PHASE 5: Secondary Interrupt Service Routine (AY) + * Handles the second injection stage if multi-patching is active. + */ +ISR(PIN_AY_INTERRUPT_VECTOR) { + if (--impulse == 0) { + __builtin_avr_delay_cycles(BIT_OFFSET_2_CYCLES); - /* - * PHASE 3: Edge Trigger - * Capture the moment AX go HIGH. - * Edge Triger: | - * AX: _-_-_-_-_-________________-_-_-_-_-_-__ - */ - #ifdef LOW_TRIGGER - while (PIN_AX_READ); - #else - while (! PIN_AX_READ); + PIN_DX_OUTPUT; + __builtin_avr_delay_cycles(OVERRIDE_2_CYCLES); + PIN_DX_INPUT; + + PIN_AY_INTERRUPT_DISABLE; + PIN_LED_OFF; + patch = 2; // Signal Phase 5 completion + } +} +#endif + +void Bios_Patching(void) { + + // --- 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(10); // 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 - /* - * PHASE 4: Precision Bit Alignment - * Delay to shift from AX address edge to the DX data bit. - * BIT_OFFSET: |-------//-----| - * AX: _-_-_-_-_-________________-_-_-_-_//_-_-_-_ - */ - _delay_us(BIT_OFFSET); + uint8_t current_confirms = 0; + //uint16_t count; - /* - * PHASE 5: Data Bus Overdrive (The Patch) - * Briefly forcing PIN_DX to OUTPUT to pull the line and "nullify" the target bit. - * This effectively overwrites the BIOS data on-the-fly - * before reverting the pin to INPUT to release the bus. - */ - PIN_DX_OUTPUT; // Force line (Low/High-Z override) - _delay_us(OVERRIDE); - PIN_DX_INPUT; // Release bus immediately - PIN_LED_OFF; - sei(); // Restore global interrupts + patch = 0; // Reset sync flag + sei(); // Enable Global Interrupts + PIN_AX_INPUT; // Set AX to monitor mode - - patch_done = 1; - } - - #endif - - #ifdef HIGH_PATCH_A - - - ISR(PIN_AY_INTERRUPT_VECTOR){ - - pulse_counter++; - if (pulse_counter == 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; - } + // --- PHASE 1: STABILIZATION & ALIGNMENT --- + // Align execution pointer to a known rising edge state. + if (PIN_AX_READ != 0) { + while (WAIT_AX_FALLING); // Wait if bus is busy + while (WAIT_AX_RISING); // Sync with next pulse start + } else { + while (WAIT_AX_RISING); // Sync with upcoming pulse } - void Bios_Patching(){ - PIN_DX_INPUT; - cli(); // Disable interrupts for timing integrity - - if (PIN_AX_READ != 0) - { - while (PIN_AX_READ != 0); - while (PIN_AX_READ == 0); - } - else - { - while (PIN_AX_READ == 0); - } - - - _delay_ms(BOOT_OFFSET); - PIN_LED_ON; - while (! PIN_AX_READ); - _delay_us(BIT_OFFSET); - PIN_DX_SET; - PIN_DX_OUTPUT; // Force line (Low/High-Z override) - _delay_us(OVERRIDE); - PIN_DX_CLEAR; - PIN_DX_INPUT; // Release bus immediately - PIN_LED_OFF; - sei(); // Restore global interrupts - - - - PIN_LED_OFF; - - - _delay_ms(FOLLOWUP_OFFSET); - - PIN_AY_INTERRUPT_RISING; - PIN_AY_INTERRUPT_ENABLE; - while (patch_done != 2); + // --- PHASE 2: SILENCE DETECTION --- + // Validate the exact number of silence windows to identify the boot stage. + while (current_confirms < CONFIRM_COUNTER_TARGET) { + uint16_t count = SILENCE_THRESHOLD; + while (count > 0) { + if (PIN_AX_READ != 0) { + while (WAIT_AX_FALLING); + break; // Impulse detected: retry current silence block + } + #ifdef IS_32U4_FAMILY + __asm__ __volatile__ ("nop"); + #endif + count--; + } + if (count == 0) { + current_confirms++; // Validated one silence window + } } - #endif + // --- PHASE 3: LAUNCH HARDWARE COUNTING (AX) --- + impulse = PULSE_COUNT; + PIN_LED_ON; + PIN_AX_INTERRUPT_CLEAR; + PIN_AX_INTERRUPT_RISING; // Setup rising-edge trigger + PIN_AX_INTERRUPT_ENABLE; // Engage ISR -#ifdef HIGH_PATCH_B + while (patch != 1); + // --- PHASE 4 & 5: SECONDARY PATCHING SEQUENCE --- + #ifdef PHASE_TWO_PATCH + PIN_AY_INPUT; + current_confirms = 0; + impulse = PULSE_COUNT_2; + // Monitor for the specific silent gap before the second patch window + while (current_confirms < CONFIRM_COUNTER_TARGET_2) { + uint16_t count = SILENCE_THRESHOLD; + while (count > 0) { + if (PIN_AX_READ != 0) { + while (WAIT_AX_FALLING); + break; + } + #ifdef IS_32U4_FAMILY + __asm__ __volatile__ ("nop"); + #endif - void Bios_Patching(){ - PIN_DX_INPUT; - cli(); // Disable interrupts for timing integrity - - if (PIN_AX_READ != 0) - { - while (PIN_AX_READ != 0); - while (PIN_AX_READ == 0); - } - else - { - while (PIN_AX_READ == 0); - } - - - _delay_ms(BOOT_OFFSET); - //PIN_LED_ON; - while (! PIN_AX_READ); - _delay_us(BIT_OFFSET); - PIN_DX_SET; - PIN_DX_OUTPUT; // Force line (Low/High-Z override) - _delay_us(OVERRIDE); - PIN_DX_CLEAR; - PIN_DX_INPUT; // Release bus immediately - PIN_LED_OFF; - sei(); // Restore global interrupts - - - - //PIN_LED_OFF; - PIN_LED_ON; - while (PIN_AY_READ != 0); - _delay_ms(FOLLOWUP_OFFSET); - - while (PIN_AY_READ); - _delay_us (BIT_OFFSET_2); - PIN_DX_OUTPUT; - _delay_us (OVERRIDE_2); - PIN_DX_INPUT; - PIN_LED_OFF; - - } - - #endif - - #ifdef INTERRUPT_RISING_HIGH_PATCH - - ISR(PIN_AX_INTERRUPT_VECTOR) { - pulse_counter++; - if (pulse_counter == PULSE_COUNT){ - _delay_us (BIT_OFFSET); - PIN_DX_SET; - PIN_DX_OUTPUT; - _delay_us (OVERRIDE); - PIN_DX_CLEAR; - PIN_DX_INPUT; - PIN_AX_INTERRUPT_DISABLE; - - pulse_counter = 0; - patch_done = 1; - } - } - - ISR(PIN_AY_INTERRUPT_VECTOR){ - - pulse_counter++; - if (pulse_counter == 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; - } - } - - void Bios_Patching(){ - - if (PIN_AX_READ != 0) - { - while (PIN_AX_READ != 0); - while (PIN_AX_READ == 0); - } - else - { - while (PIN_AX_READ == 0); - } - - - _delay_ms(BOOT_OFFSET); - - PIN_AX_INTERRUPT_RISING; - PIN_AX_INTERRUPT_ENABLE; - - - - while (patch_done != 1); - - while (PIN_AY_READ != 0); - - - _delay_ms(FOLLOWUP_OFFSET); - PIN_LED_ON; - PIN_AY_INTERRUPT_RISING; - PIN_AY_INTERRUPT_ENABLE; - - while (patch_done != 2); -PIN_LED_OFF; - } - - #endif + count--; + } + if (count == 0) { + current_confirms++; + } + } + PIN_LED_ON; + PIN_AY_INTERRUPT_CLEAR; + PIN_AY_INTERRUPT_FALLING; + PIN_AY_INTERRUPT_ENABLE; + + while (patch != 2); // Busy-wait for secondary ISR completion + + return; + #endif + +} #endif +/* + * Portability Note: The non-ISR (polling-based) version of the code is maintained + * to facilitate porting to other platforms and architectures that may not support + * AVR-specific hardware interrupts. + * + * Stability Limitation: While the polling version is more portable, it was found + * that at 16MHz, achieving consistent DATA OVERDRIVE stability is nearly impossible + * without using a Hardware ISR. The latency and jitter of software polling at this + * frequency are too high to guarantee a sub-microsecond cycle-accurate injection. + * Therefore, for ATmega328P @ 16MHz, the ISR-driven implementation remains the + * tandard. + */ + +// #ifdef BIOS_PATCH + +// volatile uint8_t impulse = 0; +// volatile uint8_t patch = 0; + + +// ISR(PIN_AX_INTERRUPT_VECTOR) { +// //impulse--; +// if (--impulse == 0){ // If impulse reaches the value defined by TRIGGER, the following actions are performed: +// // Precise cycle-accurate delay before triggering +// __builtin_avr_delay_cycles(BIT_OFFSET_CYCLES); + +// #ifdef PHASE_TWO_PATCH +// PIN_DX_SET; +// #endif + +// PIN_DX_OUTPUT; // Pull the line (Override start) +// __builtin_avr_delay_cycles(OVERRIDE_CYCLES); + +// #ifdef PHASE_TWO_PATCH +// PIN_DX_CLEAR; // Release the bus (Override end) +// #endif + +// PIN_DX_INPUT; + +// PIN_LED_OFF; +// PIN_AX_INTERRUPT_DISABLE; +// patch = 1; // patch is set to 1, indicating that the first patch is completed. +// } +// } + +// #ifdef PHASE_TWO_PATCH + + +// ISR(PIN_AY_INTERRUPT_VECTOR){ + +// //impulse--; +// if (--impulse == 0) // If impulse reaches the value defined by TRIGGER2, the following actions are performed: +// { +// __builtin_avr_delay_cycles(BIT_OFFSET_2_CYCLES); + +// PIN_DX_OUTPUT; +// __builtin_avr_delay_cycles(OVERRIDE_2_CYCLES); +// PIN_DX_INPUT; + +// PIN_AY_INTERRUPT_DISABLE; +// PIN_LED_OFF; +// patch = 2; // patch is set to 2, indicating that the second patch is completed. +// } +// } +// #endif + +// // --- BIOS Patching Main Function --- +// void Bios_Patching(void) { + +// uint8_t current_confirms = 0; +// uint8_t count = 0; +// patch = 0; +// sei(); +// PIN_AX_INPUT; +// // --- PHASE 1: Signal Stabilization & Alignment (AX) --- +// if (PIN_AX_READ != 0) { +// while (WAIT_AX_FALLING); // Wait for falling edge +// while (WAIT_AX_RISING); // Wait for next rising edge to sync +// } else { +// while (WAIT_AX_RISING); // Wait for first rising edge +// } + +// // --- PHASE 2: Silence Detection (AX) --- + +// while (current_confirms < CONFIRM_COUNTER_TARGET) { +// uint16_t count = SILENCE_THRESHOLD; + +// // --- Scan for ONE continuous block of silence --- +// while (count > 0) { +// if (PIN_AX_READ != 0) { +// while (WAIT_AX_FALLING); // Pulse detected: wait for bus to clear +// break; // Reset and try a new silence block +// } +// count--; +// } + +// // If count reaches 0, a silent block is validated +// if (count == 0) { +// current_confirms++; +// } +// } +// impulse = PULSE_COUNT; +// PIN_LED_ON; +// PIN_AX_INTERRUPT_RISING; +// PIN_AX_INTERRUPT_ENABLE; +// while (patch != 1); // Wait for the first stage of the patch to complete: + +// //PIN_LED_OFF; +// // -------- Secondary Patch ---------- +// #ifdef PHASE_TWO_PATCH + +// current_confirms = 0; +// while (current_confirms < CONFIRM_COUNTER_TARGET_2) { +// uint16_t count = SILENCE_THRESHOLD; + +// while (count > 0) { +// if (PIN_AX_READ != 0) { + +// while (WAIT_AX_FALLING); +// break; +// } +// count--; +// } + +// if (count == 0) { +// current_confirms++; +// } +// } + +// PIN_LED_ON; +// impulse = PULSE_COUNT_2; +// PIN_AY_INTERRUPT_RISING; +// PIN_AY_INTERRUPT_ENABLE; + +// while (patch != 2); // Wait for the second stage of the patch to complete: + +// #endif + +// cli(); +// } +// #endif + + diff --git a/PSNee/MCU.h b/PSNee/MCU.h index ae1ff4b..d4a8163 100644 --- a/PSNee/MCU.h +++ b/PSNee/MCU.h @@ -93,14 +93,14 @@ #pragma once -<<<<<<< Updated upstream -======= #if defined(__AVR_ATmega328__) || defined(__AVR_ATmega328A__) || \ defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PA__) || \ defined(__AVR_ATmega328PB__) || defined(__AVR_ATmega168__) || \ defined(__AVR_ATmega168A__) || defined(__AVR_ATmega168P__) || \ defined(__AVR_ATmega168PA__) || defined(__AVR_ATmega168PB__) || \ defined(__AVR_ATmega128A__) || defined(__AVR_ATmega128PB__) + +#define IS_328_168_FAMILY /*------------------------------------------------------------------------------------------------ * FUNCTION : optimizePeripherals() * @@ -113,59 +113,15 @@ * 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. ------------------------------------------------------------------------------------------------*/ -#define IS_328_168_FAMILY static inline void OptimizePeripherals(void) { // 1. Disable Interrupts during setup cli(); ->>>>>>> Stashed changes -#ifdef ATmega328_168 + // 2. Analog Modules Shutdown (Critical for Power) + ADCSRA &= ~(1 << ADEN); // Disable ADC + ACSR |= (1 << ACD); // Disable Analog Comparator -<<<<<<< Updated upstream -static inline void optimizePeripherals(void) { - // Configuring Port C (A0-A5) as Digital Inputs - // DDRC at 0 = Input. Ensure that the first 6 bits are 0. - DDRC &= ~0x3F; - - // Disable the ADC (Analog-to-Digital Converter) - // ADEN at 0 disables the module. PRADC at 1 disables the module's clock. - ADCSRA &= ~(1 << ADEN); - PRR |= (1 << PRADC); - - // Configure DIDR0 (Digital Input Disable Register) - // To read digitally via PINC, the bits of DIDR0 MUST be set to 0. - // (0 = Digital Buffer enabled) - DIDR0 &= ~0x3F; - - // Stop Timer 0 (Stops Arduino millis/micros) - // Setting TCCR0B to 0 stops the clock source. Setting TIMSK0 to 0 disables interrupts. - TCCR0B = 0; - //#define F TIMSK0 = 0; - - // Disable the Analog Comparator (Frees up resources on PD6/PD7) - // ACD at 1 = Comparator off. - ACSR |= (1 << ACD); -} - - // Define the clock speed for the microcontroller - #define F_CPU 16000000L - - // Clear the timer count register (TCNT0) - //#define TIMER_TCNT_CLEAR TCNT0 = 0x00 // TCNT0 - Timer/Counter Register, clears the timer count - - // Set OCR0A to achieve a 100KHz clock frequency - //#define SET_OCROA_DIV OCR0A = 159; // OCR0A – Output Compare Register A, 100KHz clock generation, 0x10011111 - - // Configure Timer/Counter 0 for CTC mode and enable the clock source - //#define SET_TIMER_TCCROA TCCR0A |= (1 << WGM01); // TCCR0A – Timer/Counter Control Register A, enable CTC mode (WGM01) - //#define SET_TIMER_TCCROB TCCR0B |= (1 << CS00); // TCCR0B – Timer/Counter Control Register B, set clock source to I/O clock - //Waveform Generation Mode, Mode 2 CTC - // Interrupt vector for timer compare match event - //#define CTC_TIMER_VECTOR TIMER0_COMPA_vect //interrupt vector for match event, OCR0A comparison and Timer/Counter 0 -======= - // 3. Digital Input Disable (Set bits to 1 to DISABLE) - // Disconnects digital buffers on analog pins to stop leakage DIDR0 = 0xFF; // Pins A0 to A7 #if defined(__AVR_ATmega128PB__) DIDR2 = 0xFF; @@ -228,8 +184,6 @@ static inline void optimizePeripherals(void) { TCCR4B = 0; TIMSK4 = 0; // Timer 4 (série PB) #endif - } ->>>>>>> Stashed changes #include #include @@ -238,15 +192,10 @@ static inline void optimizePeripherals(void) { #include #include - // Global interrupt control settings - #define GLOBAL_INTERRUPT_ENABLE SREG |= (1 << 7) // Set the I-bit (bit 7) in the Status Register to enable global interrupts - #define GLOBAL_INTERRUPT_DISABLE SREG &= ~(1 << 7) // Clear the I-bit (bit 7) in the Status Register to disable global interrupts + } - // Enable/Disable timer interrupts - //#define TIMER_INTERRUPT_ENABLE TIMSK0 |= (1 << OCIE0A) // Enable interrupt on Timer0 Compare Match A - //#define TIMER_INTERRUPT_DISABLE TIMSK0 &= ~(1 << OCIE0A) // Disable interrupt on Timer0 Compare Match A - // Main pin configuration for input and output + // Main pin configuration // Define the main pins as inputs #define PIN_DATA_INPUT DDRB &= ~(1 << DDB0) // Set DDRB register to configure PINB0 as input @@ -254,82 +203,78 @@ static inline void optimizePeripherals(void) { #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 - #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) + // 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_5500) || defined(SCPH_5000) || defined(SCPH_3500) || defined(SCPH_3000) || defined(SCPH_1000) + // --- 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) - // Clear the timer interrupt flag - //#define TIMER_TIFR_CLEAR TIFR0 |= (1 << OCF0A) // Clear the Timer0 Compare Match A interrupt flag + // Address (AX) and Data (DX) lines for BIOS override + #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) - // 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 PIN_AX_INPUT DDRD &= ~(1 << DDD2) + #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))) - // Define output pins for the BIOS patch - #define PIN_DX_OUTPUT DDRD |= (1 << DDD4) // Set DDRD register to configure PIND4 as output + // Hardware Interrupt (INT0) for AX pulse counting + #define PIN_AX_INTERRUPT_ENABLE EIMSK |= (1<>>>>>> Stashed changes #include #include @@ -403,103 +339,99 @@ static inline void optimizePeripherals(void) { #include #include - // Globale interrupt seting - // #define GLOBAL_INTERRUPT_ENABLE SREG |= (1 << 7) - #define GLOBAL_INTERRUPT_DISABLE SREG &= ~(1 << 7) - // #define TIMER_INTERRUPT_ENABLE TIMSK0 |= (1 << OCIE0A) - // #define TIMER_INTERRUPT_DISABLE TIMSK0 &= ~(1 << OCIE0A) - // Handling the main pins + // --- Main Bus Interface (CD-ROM Controller) --- - // 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 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) + // 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_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_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) + + #define PIN_AX_INPUT DDRD &= ~(1 << DDD1) // AX on PD1 (INT1) + #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 + #define PIN_AX_INTERRUPT_CLEAR EIFR |= (1 << INTF1) + + // Secondary Address line (AY) for multi-stage patching (INT0) + #if defined(SCPH_3000) || defined(SCPH_1000) + + #define PIN_AY_INPUT DDRD &= ~(1 << DDD0) // AY on PD0 (INT0) + #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 + #define PIN_AY_INTERRUPT_CLEAR EIFR |= (1 << INTF0) #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 -<<<<<<< Updated upstream -#ifdef ATtiny85_45_25 -======= + #if defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny25__) #define IS_ATTINY_FAMILY ->>>>>>> Stashed changes - #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 + static inline void OptimizePeripherals(void) { + // 1. Global Interrupt Disable during reconfiguration + cli(); -<<<<<<< Updated upstream -======= // 2. Analog Modules Shutdown ADCSRA = 0; // Power off ADC completely ACSR |= (1 << ACD); // Disable Analog Comparator @@ -527,7 +459,7 @@ static inline void optimizePeripherals(void) { WDTCR = 0x00; } ->>>>>>> Stashed changes + #include @@ -537,58 +469,55 @@ static inline void optimizePeripherals(void) { #include #include - // Globale interrupt seting - #define GLOBAL_INTERRUPT_ENABLE SREG |= (1 << 7) - #define GLOBAL_INTERRUPT_DISABLE SREG &= ~(1 << 7) + // --- Main Bus Interface (CD-ROM Controller) --- - // Handling the main pins + // 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 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 + // 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) - #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) || \ + defined(SCPH_7500_9000) || \ + defined(SCPH_100) || \ + defined(SCPH_102) + #error "ATtiny85/45/25 architecture is not compatible with the BIOS patch feature." + #endif #endif // ***************************************************************************************************************** diff --git a/PSNee/PSNee.ino b/PSNee/PSNee.ino index 85aba6e..cd95da4 100644 --- a/PSNee/PSNee.ino +++ b/PSNee/PSNee.ino @@ -1,19 +1,6 @@ -// PSNee-8.7 +// PSNee-V9.0 /*------------------------------------------------------------------------------------------------ -<<<<<<< Updated upstream - MCU selection -------------------------------------------------------------------------------------------------*/ - -// MCU // Arduino -//------------------------------------------------------------------------------------------------ -#define ATmega328_168 // Nano, Pro Mini, Uno -//#define ATmega32U4_16U4 // Micro, Pro Micro -//#define ATtiny85_45_25 // ATtiny - -/*------------------------------------------------------------------------------------------------ -======= ->>>>>>> Stashed changes Console selection -------------------------------------------------------------------------------------------------- @@ -22,12 +9,8 @@ SCPH model number // region code | region -------------------------------------------------------------------------------------------------*/ -<<<<<<< Updated upstream -//#define SCPH_xxx1 // NTSC U/C | America. -======= -//#define SCPH_xxxx // | Universal. + #define SCPH_xxx1 // NTSC U/C | America. ->>>>>>> Stashed changes //#define SCPH_xxx2 // PAL | Europ. //#define SCPH_xxx3 // NTSC J | Asia. //#define SCPH_xxxx // Universal @@ -56,9 +39,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_5500 // DX - D0 | AX - A5 | | 3.0j - CRC FF3EEB8C -//#define SCPH_5000 // DX - D0 | AX - A5 | AX - A4 | 2.2j - CRC 24FC7E17 -//#define SCPH_3500 // DX - D0 | AX - A5 | AX - A4 | 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 @@ -81,9 +62,9 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX /*------------------------------------------------------------------------------------------------ Hysteresis ------------------------------------------------------------------------------------------------*/ -#define HYSTERESIS_MAX 15 // All model. -//#define HYSTERESIS_MAX 25 // Only FAT! For models with problematic CD players. -// Determines the number of times the data in the DATA line must match the filter of the region code injection function to trigger the injection. +#define HYSTERESIS_MAX 25 // Coupled with hysteresis-reset post-injection; allows for a + // wider tuning range without drifting out of the sweet spot + // between hysteresis_max and hysteresis_reset. /*------------------------------------------------------------------------------------------------ Summary of practical information. Fuses. Pinout @@ -136,28 +117,13 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX //Flag initializing for automatic console generation selection 0 = old, 1 = pu-22 end ++ uint8_t wfck_mode = 0; - -// --- Prototypes (Forward declarations) --- -// These tell the compiler that the functions exist later in the code. -void logic_Standard(uint8_t isDataSector); -void logic_SCPH_5903(uint8_t isDataSector); - -// Function pointer type definition for the console detection logic. -// This allows switching between 'Standard' and 'SCPH-5903' heuristics dynamically. -typedef void (*ConsoleLogicPtr)(uint8_t isDataSector); - -// Global pointer holding the currently active logic function. -// Using a function pointer eliminates the need for repetitive 'if/else' checks in the main loop. -volatile ConsoleLogicPtr currentLogic = logic_Standard; - // Variables de contrôle globales -uint8_t scbuf[12] = { 0 }; +// Global buffer to store the 12-byte SUBQ channel data +uint8_t subqBuffer[12]; uint8_t hysteresis = 0; //Initializing values ​​for region code injection timing #define DELAY_BETWEEN_BITS 4000 // 250 bits/s (microseconds) (ATtiny 8Mhz works from 3950 to 4100) PU-23 PU-22 MAX 4250 MIN 3850 -#define DELAY_BETWEEN_INJECTIONS 90 // The sweet spot is around 80~100. For all observed models, the worst minimum time seen is 72, and it works well up to 250. - /*------------------------------------------------------------------------------------------------ Code section @@ -165,440 +131,386 @@ 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: +void BoardDetection() { + // Default state: 0 (Static/GATE mode for PU-7 to PU-20) + wfck_mode = 0; - WFCK: __----------------------- // CONTINUOUS (PU-7 .. PU-20)(GATE) + /** + * 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); - WFCK: __-_-_-_-_-_-_-_-_-_-_-_- // FREQUENCY (PU-22 or newer) + // Define a sampling window to capture potential oscillations + uint16_t detectionWindow = 10000; + + while (--detectionWindow) { + /** + * 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) { + // Software debounce to filter out micro-glitches or parasitic noise + uint8_t debounce = 100; + while (--debounce); - 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() { - 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 + /** + * 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 confirmed: PU-22 or newer (Frequency mode) 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(debounce, 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) { +/****************************************************************************************************************** + * 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). + ******************************************************************************************************************/ - uint8_t scpos = 0; - //uint8_t bitbuf = 0; +void CaptureSUBQ(void) { + uint8_t bytesRemaining = 12; // Total frame size for a complete SUBQ + uint8_t* bufferPtr = subqBuffer; do { - uint8_t bitbuf = 0; - for (uint8_t i = 0; i < 8; i++) { - // Wait for Clock to go LOW then HIGH (Sampling on 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: 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 - // Shift buffer and sample the SUBQ pin - bitbuf >>= 1; + /** + * 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) { - bitbuf |= 0x80; + currentByte |= 0x80; } } - scbuf[scpos++] = bitbuf; - } while (scpos < 12); + // Store reconstructed byte and advance pointer + *bufferPtr++ = currentByte; + } while (--bytesRemaining); // Efficient countdown for AVR binary size + + #if defined(PSNEE_DEBUG_SERIAL_MONITOR) + LogSUBQ(subqBuffer); + #endif } -/************************************************************************************** - * Processes sector data for the SCPH-5903 (Dual-interface PS1) to differentiate - * between PlayStation games and Video CDs (VCD). - * - * This heuristic uses an 'hysteresis' counter to stabilize disc detection: - * - Increases when a PSX Lead-In or valid game sector is identified. - * - Remains neutral/ignores VCD-specific Lead-In patterns. - * - Decreases (fades out) when the data does not match known patterns. - * - * isDataSector Boolean flag indicating if the current sector contains data. - -**************************************************************************************/ -// void logic_SCPH_5903(uint8_t isDataSector) { -// // Identify VCD Lead-In: Specific SCBUF patterns (0xA0/A1/A2) with sub-mode 0x02 -// uint8_t isVcdLeadIn = isDataSector && scbuf[1] == 0x00 && scbuf[6] == 0x00 && -// (scbuf[2] == 0xA0 || scbuf[2] == 0xA1 || scbuf[2] == 0xA2) && -// (scbuf[3] == 0x02); -// // Identify PSX Lead-In: Same SCBUF patterns but different sub-mode (!= 0x02) -// uint8_t isPsxLeadIn = isDataSector && scbuf[1] == 0x00 && scbuf[6] == 0x00 && -// (scbuf[2] == 0xA0 || scbuf[2] == 0xA1 || scbuf[2] == 0xA2) && -// (scbuf[3] != 0x02); - -// if (isPsxLeadIn) { -// hysteresis++; -// } -// else if (hysteresis > 0 && !isVcdLeadIn && -// ((scbuf[0] == 0x01 || isDataSector) && scbuf[1] == 0x00 && scbuf[6] == 0x00)) { -// hysteresis++; // Maintain/Increase confidence for valid non-VCD sectors -// } -// else if (hysteresis > 0) { -// hysteresis--; // Patterns stop matching -// } -// } - -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; - } - } - - // Patterns stop matching or VCD Lead-In detected: decrease confidence - if (hysteresis > 0) { - hysteresis--; - } -} /****************************************************************************************** - * Heuristic logic for standard PlayStation hardware (Non-VCD models). + * FUNCTION : Filter_SUBQ_Samples() [SCPH_5903 Dual-Interface Variant] * - * 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 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 FilterSUBQSamples(uint8_t isDataSector) { + uint8_t currentHysteresis = hysteresis; -// void logic_Standard(uint8_t isDataSector) { -// // Detect specific Lead-In patterns -// if ((isDataSector && scbuf[1] == 0x00 && scbuf[6] == 0x00) && -// (scbuf[2] == 0xA0 || scbuf[2] == 0xA1 || scbuf[2] == 0xA2 || -// (scbuf[2] == 0x01 && (scbuf[3] >= 0x98 || scbuf[3] <= 0x02)))) { -// hysteresis++; -// } -// // Maintain confidence if general valid sector markers are found -// else if (hysteresis > 0 && -// ((scbuf[0] == 0x01 || isDataSector) && scbuf[1] == 0x00 && scbuf[6] == 0x00)) { -// hysteresis++; -// } -// else if (hysteresis > 0) { -// hysteresis--; -// } -// } + // --- 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]; -void logic_Standard(uint8_t isDataSector) { - // Optimization: Check common markers once (scbuf[1] and scbuf[6]) - if (scbuf[1] == 0x00 && scbuf[6] == 0x00) { - - // 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++; + /** + * 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; } } - // No valid pattern found: decrease confidence - if (hysteresis > 0) { - hysteresis--; + // --- 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 + +/****************************************************************************************** + * FUNCTION : Filter_SUBQ_Samples() + * + * 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 FilterSUBQSamples(uint8_t isDataSector) { + uint8_t currentHysteresis = hysteresis; + + // --- 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]; + + /* + * 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)) ) + { + hysteresis = currentHysteresis + 1; + return; + } + } + + // --- STEP 2: Signal Decay / Missed Hits --- + // Reduce the hit counter if the current SUBQ sample fails validation. + if (currentHysteresis > 0) { + hysteresis = currentHysteresis - 1; + } + } + +#endif /********************************************************************************************* - * 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 +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] = { - { 0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11011010, 0b00000010 }, // SCEI - { 0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11111010, 0b00000010 }, // SCEA - { 0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11101010, 0b00000010 } // SCEE + { 0x59, 0xC9, 0x4B, 0x5D, 0xDA, 0x02 }, // SCEI + { 0x59, 0xC9, 0x4B, 0x5D, 0xFA, 0x02 }, // SCEA + { 0x59, 0xC9, 0x4B, 0x5D, 0xEA, 0x02 } // SCEE }; - hysteresis = 11; + + hysteresis = (HYSTERESIS_MAX - 10); // Reset hysteresis to mid-point after triggering #ifdef LED_RUN PIN_LED_ON; #endif - // Pin initialization + // Cache wfck_mode to save CPU cycles during the bit loop + uint8_t isWfck = wfck_mode; PIN_DATA_OUTPUT; PIN_DATA_CLEAR; - if (!wfck_mode) { - PIN_WFCK_OUTPUT; PIN_WFCK_CLEAR; + // Legacy boards require the chip to drive the simulated WFCK/Gate + if (!isWfck) { + PIN_WFCK_OUTPUT; + PIN_WFCK_CLEAR; } - - _delay_ms(DELAY_BETWEEN_INJECTIONS); - // Injection loop (3 cycles) + // Loop through the selected region(s) 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]; + const uint8_t* bytePtr = allRegionsSCEx[regionIndex]; + + uint8_t currentByte = *bytePtr++; + uint8_t bitIdx = 0; - // 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; + // Process the 44-bit SCEx stream + for (uint8_t totalBits = 44; totalBits > 0; totalBits--) { + uint8_t currentBit = currentByte & 0x01; + currentByte >>= 1; + bitIdx++; - if (!wfck_mode) { - // LOGIC GATE MODE (Old boards) - if (currentBit == 0) { - PIN_DATA_OUTPUT; PIN_DATA_CLEAR; - } - else { - PIN_DATA_INPUT; // High Impedance = 1 - } + // Reload next byte every 8 bits (except the last partial one) + if (bitIdx == 8 && totalBits > 4) { + currentByte = *bytePtr++; + bitIdx = 0; + } + + if (currentBit == 0) { + // BIT 0: Pull DATA low- + PIN_DATA_CLEAR; + if (!isWfck) PIN_DATA_OUTPUT; _delay_us(DELAY_BETWEEN_BITS); - } - else { - // WFCK MODULATION (Newer boards / PU-18+) - if (currentBit == 0) { - PIN_DATA_CLEAR; + } 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 { - // 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; - } + } 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); // Wait for Falling Edge + PIN_DATA_CLEAR; + while (!PIN_WFCK_READ); // Wait for Rising Edge + PIN_DATA_SET; } } } } - // Inter-string silence + + /* + EXIT CONDITION: + If we are NOT in Universal mode (3), we stop after the first + successful region injection. + */ + if (injectSCEx != 3) { + + PIN_DATA_OUTPUT; + PIN_DATA_CLEAR; + + if (!isWfck) { + PIN_WFCK_INPUT; + PIN_DATA_INPUT; + } + break; + } + + + // Clean up state between region cycles PIN_DATA_OUTPUT; PIN_DATA_CLEAR; - _delay_ms(DELAY_BETWEEN_INJECTIONS); + _delay_ms(180); + } - // Cleanup: Set pins to High-Z (Safe mode) - if (!wfck_mode) { - PIN_WFCK_INPUT; - PIN_DATA_INPUT; + // Restore pins to Input/High-Z to avoid signal interference after injection + if (!isWfck) { + PIN_WFCK_INPUT; + PIN_DATA_INPUT; } + #ifdef LED_RUN - PIN_LED_OFF; + PIN_LED_OFF; + #endif + + + #if defined(PSNEE_DEBUG_SERIAL_MONITOR) + DebugInject(); #endif } - - -// 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 }, // NTSC-J SCEI SCPH-xxx0 SCPH-xxx3 -// { 0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11111010, 0b00000010 }, // NTSC-U/C SCEA SCPH-xxx1 -// { 0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11101010, 0b00000010 } // PAL SCEE SCPH-xxx2 -// }; - -// // if (hysteresis < HYSTERESIS_MAX) return; - - -// 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]; - -// for (uint8_t bit_counter = 0; bit_counter < 44; bit_counter++) { -// // Bit-level extraction: -// // 1. bit_counter >> 3 identifies the byte index (integer division by 8). -// // 2. bit_counter & 0x07 identifies the bit position within that byte (modulo 8). -// // 3. Right-shift and mask (& 0x01) isolates the target bit for injection. -// uint8_t currentByte = ByteSet[bit_counter >> 3]; -// uint8_t currentBit = (currentByte >> (bit_counter & 0x07)) & 0x01; - -// 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+) -// // PIN_DATA_OUTPUT; -// if (currentBit == 0) { -// PIN_DATA_CLEAR; -// _delay_us(DELAY_BETWEEN_BITS); -// } -// else { -// // Synchronize injection with WFCK clock edges -// uint8_t count = 30; -// uint8_t last_wfck = PIN_WFCK_READ; -// while (count > 0) { -// uint8_t current_wfck = PIN_WFCK_READ; -// if (current_wfck != last_wfck) { -// if (current_wfck) { -// PIN_DATA_SET; count--; -// } -// else { -// PIN_DATA_CLEAR; -// } -// last_wfck = current_wfck; -// } -// } -// } -// } -// } - -// // 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 -// } - void Init() { -#if defined(ATmega328_168) - optimizePeripherals(); -#endif + // --- Hardware Power & Peripheral Optimization --- -#ifdef LED_RUN - PIN_LED_OUTPUT; -#endif + OptimizePeripherals(); -#ifdef BIOS_PATCH - uint8_t Flag_Switch = 0; - GLOBAL_INTERRUPT_ENABLE; -#ifdef SCPH_7000 - PIN_SWITCH_INPUT; - PIN_SWITCH_SET; - if (PIN_SWITCH_READ == 0){ - Flag_Switch =1; - } -#endif -#ifdef LED_RUN - //PIN_LED_ON; -#endif + #ifdef LED_RUN + PIN_LED_OUTPUT; + #endif - if (Flag_Switch == 0) { - Bios_Patching(); - } + // --- Critical Boot Patching --- + #ifdef BIOS_PATCH -#ifdef LED_RUN - //PIN_LED_OFF; -#endif +// #ifdef LED_RUN +// PIN_LED_ON; +// #endif -#endif + // Execute BIOS patching + Bios_Patching(); + + cli(); + +// #ifdef LED_RUN +// PIN_LED_OFF; +// #endif + #endif - GLOBAL_INTERRUPT_DISABLE; - PIN_SQCK_INPUT; PIN_SUBQ_INPUT; -<<<<<<< Updated upstream -#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 -#elif defined(PSNEE_DEBUG_SERIAL_MONITOR) && !defined(ATtiny85_45_25) - Serial.begin(500000); // 60 bytes in 12ms (expected data: ~26 bytes / 12ms) // update: this is actually quicker -#endif -} -======= // --- Debug Interface Setup --- #if defined(PSNEE_DEBUG_SERIAL_MONITOR) && defined(IS_ATTINY_FAMILY) //pinMode(debugtx, OUTPUT); // software serial tx pin @@ -609,60 +521,36 @@ void Init() { // --- Console Analysis --- // Identify board revision (PU-7 to PU-22+) to set correct injection timings - BoardBetection(); + BoardDetection(); } ->>>>>>> Stashed changes int main() { Init(); - -#ifdef SCPH_5903 - currentLogic = logic_SCPH_5903; - #else - currentLogic = logic_Standard; -#endif - board_detection(); - -#if defined(PSNEE_DEBUG_SERIAL_MONITOR) - Debug_Log(lows, wfck_mode); -#endif - while (1) { - _delay_ms(1); /* Start with a small delay, which can be necessary - in cases where the MCU loops too quickly and picks up the laster SUBQ trailing end*/ - captureSubQ(); + // 1. Timing Sync: Prevent reading the tail end of the previous SUBQ packet + _delay_ms(1); -#if defined(PSNEE_DEBUG_SERIAL_MONITOR) - Debug_Scbuf(scbuf); -#endif + // 2. Data Acquisition: Capture 12-byte SUBQ channel stream into buffer + CaptureSUBQ(); - /*------------------------------------------------------------------------------- - 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. - -------------------------------------------------------------------------------*/ + // 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); - //This variable initialization macro is to replace (0x41) with a filter that will check that only the three most significant bits are correct. 0x001xxxxx - uint8_t isDataSector = (((scbuf[0] & 0x40) == 0x40) && (((scbuf[0] & 0x10) == 0) && ((scbuf[0] & 0x80) == 0))); - // Execute selected logic through function pointer - currentLogic(isDataSector); - - - if (hysteresis >= HYSTERESIS_MAX) { - performInjectionSequence(INJECT_SCEx); - } - - -#if defined(PSNEE_DEBUG_SERIAL_MONITOR) - Debug_Inject(); -#endif + // Execute selected logic + FilterSUBQSamples(isDataSector); + // 5. Execution: Trigger SCEx injection once confidence (hysteresis) is reached + if (hysteresis >= HYSTERESIS_MAX) { + PerformInjectionSequence(INJECT_SCEx); + } } + return 0; } diff --git a/PSNee/settings.h b/PSNee/settings.h index 4d41734..6d1ee64 100644 --- a/PSNee/settings.h +++ b/PSNee/settings.h @@ -22,8 +22,7 @@ // Results of the maximum values // tested with an Atmega328P -<<<<<<< Updated upstream -======= + #if defined(IS_32U4_FAMILY) // ------ SCPH 100 / 102 ------ @@ -36,184 +35,179 @@ #define BIT_OFFSET_CYCLES 47 //60 #define OVERRIDE_CYCLES 3 #endif ->>>>>>> Stashed changes -// #ifdef SCPH_102 -// #define BIOS_PATCH -// #define INTERRUPT_RISING -// #define BOOT_OFFSET 83.9 -// #define PULSE_COUNT 48 !!! -1 -// #define BIT_OFFSET 2.75 -// #define OVERRIDE 0.2 -// #endif + // // -------- SCPH 7500 / 9000 -------- + #ifdef SCPH_7500_9000 + #define BIOS_PATCH + #define SILENCE_THRESHOLD 1100 + #define CONFIRM_COUNTER_TARGET 1 + #define PULSE_COUNT 15 //15 + #define BIT_OFFSET_CYCLES 47 //60 + #define OVERRIDE_CYCLES 3 + #endif -// #ifdef SCPH_100 -// #define BIOS_PATCH -// #define INTERRUPT_RISING -// #define BOOT_OFFSET 83.9 //83.72 - 84.15 -// #define PULSE_COUNT 47 -// #define BIT_OFFSET 2.75 //2.63 - 2.87 -// #define OVERRIDE 0.2 -// #endif -#ifdef SCPH_102 -#define BIOS_PATCH -#define TEST_BIOS -#define BOOT_OFFSET 83.9 // Stabilization window (ms) -#define BIT_OFFSET 225.6 // Precision data alignment (us) -#define OVERRIDE 0.2 // DX injection width (us) + // -------- SCPH 7000 -------- + #ifdef SCPH_7000 + #define BIOS_PATCH + #define SILENCE_THRESHOLD 1100 + #define CONFIRM_COUNTER_TARGET 1 + #define PULSE_COUNT 15 + #define BIT_OFFSET_CYCLES 47 + #define OVERRIDE_CYCLES 3 + #endif + + + // // ----- SCPH 3500 / 5000 / 5500 ----- + #ifdef SCPH_3500_5500 + #define BIOS_PATCH + #define SILENCE_THRESHOLD 25000 + #define CONFIRM_COUNTER_TARGET 1 + #define PULSE_COUNT 84 //84 + #define BIT_OFFSET_CYCLES 47 //60 + #define OVERRIDE_CYCLES 3 + #endif + + // // -------- SCPH 3000 -------- + #ifdef SCPH_3000 + #define BIOS_PATCH + #define PHASE_TWO_PATCH + #define SILENCE_THRESHOLD 1100 + #define CONFIRM_COUNTER_TARGET 9 + #define PULSE_COUNT 59 + #define BIT_OFFSET_CYCLES 45 + #define OVERRIDE_CYCLES 3 + #define CONFIRM_COUNTER_TARGET_2 206 + #define PULSE_COUNT_2 42 + #define BIT_OFFSET_2_CYCLES 48 + #define OVERRIDE_2_CYCLES 3 + #endif + + + // // -------- SCPH 1000 -------- + #ifdef SCPH_1000 + #define BIOS_PATCH + #define PHASE_TWO_PATCH + #define SILENCE_THRESHOLD 1100 + #define CONFIRM_COUNTER_TARGET 9 + #define PULSE_COUNT 91 + #define BIT_OFFSET_CYCLES 45 + #define OVERRIDE_CYCLES 3 + #define CONFIRM_COUNTER_TARGET_2 222 + #define PULSE_COUNT_2 70 + #define BIT_OFFSET_2_CYCLES 48 + #define OVERRIDE_2_CYCLES 3 + #endif #endif -#ifdef SCPH_100 -#define BIOS_PATCH -#define TEST_BIOS -#define BOOT_OFFSET 83.9 // Stabilization window (ms) -#define BIT_OFFSET 225.6 // Precision data alignment (us) -#define OVERRIDE 0.2 // DX injection width (us) -#endif +#if defined(IS_328_168_FAMILY) -#ifdef SCPH_7500_9000 -#define BIOS_PATCH -#define TEST_BIOS -#define BOOT_OFFSET 75.2 -#define BIT_OFFSET 71.5 -#define OVERRIDE 0.2 -#endif + // ------ SCPH 100 / 102 ------ + #if defined(SCPH_100) || \ + defined(SCPH_102) + #define BIOS_PATCH + #define SILENCE_THRESHOLD 1500 + #define CONFIRM_COUNTER_TARGET 8 + #define PULSE_COUNT 47 //47 + #define BIT_OFFSET_CYCLES 47 //60 + #define OVERRIDE_CYCLES 3 + #endif -#ifdef SCPH_7000 -#define BIOS_PATCH -#define TEST_BIOS -#define BOOT_OFFSET 75.2 -#define BIT_OFFSET 71.5 -#define OVERRIDE 0.2 -#endif -#ifdef SCPH_5500 -#define BIOS_PATCH -#define TEST_BIOS -#define LOW_TRIGGER -#define BOOT_OFFSET 76.07 //75.99 - 76.14 -#define BIT_OFFSET 95.6 -#define OVERRIDE 0.2 -#endif + // // -------- SCPH 7500 / 9000 -------- + #ifdef SCPH_7500_9000 + #define BIOS_PATCH + #define SILENCE_THRESHOLD 1500 + #define CONFIRM_COUNTER_TARGET 1 + #define PULSE_COUNT 15 //15 + #define BIT_OFFSET_CYCLES 47 //60 + #define OVERRIDE_CYCLES 3 + #endif -#ifdef SCPH_5000 -#define BIOS_PATCH -#define TEST_BIOS -#define LOW_TRIGGER -#define BOOT_OFFSET 75.2 //75.12 - 75.27 -#define BIT_OFFSET 95.65 -#define OVERRIDE 0.2 -#endif -#ifdef SCPH_3500 -#define BIOS_PATCH -#define TEST_BIOS -#define LOW_TRIGGER -#define BOOT_OFFSET 75.2 //75.12 - 75.27 -#define BIT_OFFSET 95.4 -#define OVERRIDE 0.2 -#endif + // -------- SCPH 7000 -------- + #ifdef SCPH_7000 + #define BIOS_PATCH + #define SILENCE_THRESHOLD 1500 + #define CONFIRM_COUNTER_TARGET 1 + #define PULSE_COUNT 15 + #define BIT_OFFSET_CYCLES 47 + #define OVERRIDE_CYCLES 3 + #endif -// #ifdef SCPH_3000 -// #define BIOS_PATCH -// #define HIGH_PATCH_A -// #define BOOT_OFFSET 82.9 //82.65 - 83.26 -// #define BIT_OFFSET 283.25 -// #define OVERRIDE 0.15 -// #define HIGH_PATCH -// #define FOLLOWUP_OFFSET 253.3 -// #define PULSE_COUNT_2 43 -// #define BIT_OFFSET_2 2.88 -// #define OVERRIDE_2 0.15 -// #endif -#ifdef SCPH_3000 -#define BIOS_PATCH -#define HIGH_PATCH_B -#define BOOT_OFFSET 82.9 //82.65 - 83.26 -#define BIT_OFFSET 283.25 -#define OVERRIDE 0.15 -#define HIGH_PATCH -#define FOLLOWUP_OFFSET 253.3 -#define BIT_OFFSET_2 201.8 -#define OVERRIDE_2 0.15 -#endif + // // ----- SCPH 3500 / 5000 / 5500 ----- + #ifdef SCPH_3500_5500 + #define BIOS_PATCH + #define SILENCE_THRESHOLD 32000 + #define CONFIRM_COUNTER_TARGET 1 + #define PULSE_COUNT 84 //84 + #define BIT_OFFSET_CYCLES 47 //60 + #define OVERRIDE_CYCLES 3 + #endif -// #ifdef SCPH_3000 -// #define BIOS_PATCH -// #define INTERRUPT_RISING_HIGH_PATCH -// #define BOOT_OFFSET 82.9 //82.65 - 83.26 -// #define PULSE_COUNT 60 -// #define BIT_OFFSET 2.7 //2.58 - 2.8 -// #define OVERRIDE 0.15 -// #define HIGH_PATCH -// #define FOLLOWUP_OFFSET 253.3 -// #define PULSE_COUNT_2 43 -// #define BIT_OFFSET_2 2.88 -// #define OVERRIDE_2 0.15 -// #endif + // // -------- SCPH 3000 -------- + #ifdef SCPH_3000 + #define BIOS_PATCH + #define PHASE_TWO_PATCH + #define SILENCE_THRESHOLD 1500 + #define CONFIRM_COUNTER_TARGET 9 + #define PULSE_COUNT 59 + #define BIT_OFFSET_CYCLES 45 + #define OVERRIDE_CYCLES 3 + #define CONFIRM_COUNTER_TARGET_2 206 + #define PULSE_COUNT_2 42 + #define BIT_OFFSET_2_CYCLES 48 + #define OVERRIDE_2_CYCLES 3 + #endif -// #ifdef SCPH_1000 -// #define BIOS_PATCH -// #define INTERRUPT_RISING_HIGH_PATCH -// #define BOOT_OFFSET 82.9 // 82.63 - 83.26 -// #define PULSE_COUNT 92 -// #define BIT_OFFSET 2.65 // 2.58 - 2.75 -// #define OVERRIDE 0.15 -// #define HIGH_PATCH -// #define FOLLOWUP_OFFSET 272.8 -// #define PULSE_COUNT_2 71 -// #define BIT_OFFSET_2 2.88 -// #define OVERRIDE_2 0.15 -// #endif -// #ifdef SCPH_1000 -// #define BIOS_PATCH -// #define HIGH_PATCH_A -// #define BOOT_OFFSET 82.9 // 82.63 - 83.26 -// #define BIT_OFFSET 437.1 // 2.58 - 2.75 -// #define OVERRIDE 0.15 -// #define HIGH_PATCH -// #define FOLLOWUP_OFFSET 272.8 -// #define PULSE_COUNT_2 71 -// #define BIT_OFFSET_2 2.88 -// #define OVERRIDE_2 0.15 -// #endif - -#ifdef SCPH_1000 -#define BIOS_PATCH -#define HIGH_PATCH_B -#define BOOT_OFFSET 82.9 // 82.63 - 83.26 -#define BIT_OFFSET 437.1 // 2.58 - 2.75 -#define OVERRIDE 0.15 -#define HIGH_PATCH -#define FOLLOWUP_OFFSET 272.8 -#define BIT_OFFSET_2 336.05 -#define OVERRIDE_2 0.15 + // // -------- SCPH 1000 -------- + #ifdef SCPH_1000 + #define BIOS_PATCH + #define PHASE_TWO_PATCH + #define SILENCE_THRESHOLD 1500 + #define CONFIRM_COUNTER_TARGET 9 + #define PULSE_COUNT 91 + #define BIT_OFFSET_CYCLES 45 + #define OVERRIDE_CYCLES 3 + #define CONFIRM_COUNTER_TARGET_2 222 + #define PULSE_COUNT_2 70 + #define BIT_OFFSET_2_CYCLES 48 + #define OVERRIDE_2_CYCLES 3 + #endif #endif /*------------------------------------------------------------------------------------------------ Region Settings Section ------------------------------------------------------------------------------------------------*/ -#if defined(SCPH_100) || defined(SCPH_7500_9000) || defined(SCPH_7000) || \ - defined(SCPH_5500) || defined(SCPH_5000) ||defined(SCPH_3500) || defined(SCPH_3000) || \ - defined(SCPH_1000) || defined(SCPH_xxx3) || defined(SCPH_5903) - #define INJECT_SCEx 0 // NTSC-J -#endif +#if defined(SCPH_100) || \ + defined(SCPH_7500_9000) || \ + defined(SCPH_7000) || \ + defined(SCPH_3500_5500) || \ + defined(SCPH_3500) || \ + defined(SCPH_3000) || \ + defined(SCPH_1000) || \ + defined(SCPH_xxx3) || \ + defined(SCPH_5903) -#if defined(SCPH_xxx1) - #define INJECT_SCEx 1 // NTSC-U/C -#endif + #define INJECT_SCEx 0 // NTSC-J -#if defined(SCPH_xxx2) || defined(SCPH_102) - #define INJECT_SCEx 2 // PAL -#endif +#elif defined(SCPH_xxx1) + + #define INJECT_SCEx 1 // NTSC-U/C + +#elif defined(SCPH_xxx2) || \ + defined(SCPH_102) + + #define INJECT_SCEx 2 // PAL + +#elif defined(SCPH_xxxx) + + #define INJECT_SCEx 3 // Universal: NTSC-J -> NTSC-U/C -> PAL -#if defined(SCPH_xxxx) - #define INJECT_SCEx 3 // Universal: NTSC-J -> NTSC-U/C -> PAL #endif @@ -224,14 +218,13 @@ #if defined(PSNEE_DEBUG_SERIAL_MONITOR) -void Debug_Log (uint16_t Lows, int Wfck_mode){ //Information about the MCU, and old or late console mode. +void Debug_Log (uint16_t debounce, int Wfck_mode){ //Information about the MCU, and old or late console mode. -<<<<<<< Updated upstream -#if defined(ATtiny85_45_25) +#if defined(IS_ATTINY_FAMILY) mySerial.print("m "); mySerial.println(Wfck_mode); -#elif !defined(ATtiny85_45_25) +#else Serial.print(" MCU frequency: "); Serial.print(F_CPU); Serial.println(" Hz"); - Serial.print(" lows: "); Serial.println(Lows); + Serial.print(" debounce: "); Serial.println(debounce); Serial.print(" wfck_mode: "); Serial.println(Wfck_mode); Serial.print(" region: "); Serial.print(region[0]); Serial.print(region[1]); Serial.println(region[2]); #endif @@ -239,12 +232,11 @@ void Debug_Log (uint16_t Lows, int Wfck_mode){ //Information about the // 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 -#if defined(ATtiny85_45_25) +#if defined(IS_ATTINY_FAMILY) 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 -======= #if defined(IS_ATTINY_FAMILY) // --- COMPACT SYSTEM LOG (ATtiny) --- // Minimalistic output to save CPU cycles and maintain timing precision. @@ -266,7 +258,7 @@ void Debug_Scbuf (uint8_t *Scbuf){ // Data from the DATA bus Serial.println(region[2]); // Active injection string (e.g., SCEE) #endif } - +#endif /****************************************************************************************** * FUNCTION : LogSUBQ * @@ -290,47 +282,41 @@ void LogSUBQ(uint8_t *dataBuffer) { */ if (!(dataBuffer[0] == 0 && dataBuffer[1] == 0 && dataBuffer[2] == 0 && dataBuffer[3] == 0)) { -#if defined(IS_ATTINY_FAMILY) - // --- 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 for hex alignment ->>>>>>> Stashed changes + #if defined(IS_ATTINY_FAMILY) + // --- 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 for hex alignment + + } + mySerial.print(Scbuf[i, HEX]); } - mySerial.print(Scbuf[i, HEX]); + mySerial.println(""); } - 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 + #elif !defined(IS_ATTINY_FAMILY) + 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 + } + Serial.print(Scbuf[i], HEX); + Serial.print(" "); } - Serial.print(Scbuf[i], HEX); - Serial.print(" "); + Serial.println(""); } - Serial.println(""); + #endif } -#endif -} void Debug_Inject(){ // Confirmation of region code injection -<<<<<<< Updated upstream -#if defined(ATtiny85_45_25) - mySerial.print("!"); -#elif !defined(ATtiny85_45_25)|| defined(SCPH_102_legacy) -======= + #if defined(IS_ATTINY_FAMILY) // --- MINIMALIST NOTIFICATION (ATtiny) --- mySerial.print("!"); - #else // --- VERBOSE NOTIFICATION (ATmega) --- // Standard visual feedback for debugging and monitoring. ->>>>>>> Stashed changes Serial.println(" INJECT ! "); #endif } @@ -341,52 +327,20 @@ void Debug_Inject(){ // Confirmation of region code injection Compilation message -----------------------------------------------------------------------------------------------*/ -#if !defined(SCPH_xxx3) && \ - !defined(SCPH_102) && !defined(SCPH_101) && !defined(SCPH_100) && !defined(SCPH_7500_9000) && \ - !defined(SCPH_7000) && !defined(SCPH_5500) && !defined(SCPH_5000) && !defined(SCPH_3500) && !defined(SCPH_3000) && \ - !defined(SCPH_1000) && !defined(SCPH_5903) &&\ - !defined(SCPH_xxx1) && !defined(SCPH_xxx2) && !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_5500) ^ defined(SCPH_5000) ^ 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." +#ifdef LED_RUN + #pragma message "Feature: Status LED ENABLED" #endif -<<<<<<< Updated upstream -#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" +// SECURITY CHECK: Ensure only one console is selected +// If you get "not portable" warnings here, it's only because multiple models are active. +#if (defined(SCPH_1000) + defined(SCPH_3000) + defined(SCPH_3500_5500) + \ + defined(SCPH_7000) + defined(SCPH_7500_9000) + defined(SCPH_100) + \ + defined(SCPH_102) + defined(SCPH_xxx1) + defined(SCPH_xxx2) + \ + defined(SCPH_xxx3) + defined(SCPH_5903) + defined(SCPH_xxxx)) > 1 + #error "Too many consoles selected! Please uncomment ONLY ONE model." #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" -======= -// // --- MCU SELECTION CHECK --- -// #if !defined(ATmega328_168) && \ -// !defined(ATmega328_168PB) && \ -// !defined(ATmega32U4_16U4) && \ -// !defined(ATtiny85_45_25) -// #error "No MCU selected! Please choose one supported architecture." - -// #elif (defined(ATmega328_168) + \ -// defined(ATmega328_168PB) + \ -// defined(ATmega32U4_16U4) + \ -// defined(ATtiny85_45_25) > 1) -// #error "Multiple MCUs selected! Please enable only one architecture." -// #endif - -// --- RESOURCE CONFLICT CHECK (ATtiny) --- -#if defined(IS_ATTINY_FAMILY) && \ - defined(LED_RUN) && \ - defined(PSNEE_DEBUG_SERIAL_MONITOR) - #error "Resource conflict: LED_RUN and DEBUG_SERIAL are incompatible on ATtiny." -#endif - -// --- Console Model Info --- +// Show target console. #if defined(SCPH_1000) #pragma message "Target Console: SCPH-1000 (NTSC-J)" #elif defined(SCPH_3000) @@ -399,29 +353,36 @@ void Debug_Inject(){ // Confirmation of region code injection #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" + #pragma message "Target Console: SCPH_xxx1 Generic NTSC-U/C" #elif defined(SCPH_xxx2) - #pragma message "Target Console: Generic PAL" + #pragma message "Target Console: SCPH_xxx2 Generic PAL" #elif defined(SCPH_xxx3) - #pragma message "Target Console: Generic NTSC-J" + #pragma message "Target Console: SCPH_xxx3 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" + #pragma message "Target Console: SCPH_xxxx Universal Region Mode" +#else + // Error if no console is uncommented + #error "Console not selected! Please uncomment one SCPH model." #endif -// --- MCU Architecture Info --- -#if defined(IS_328_168_FAMILY) - #pragma message "Microcontroller: ATmega328/168 (Arduino Nano/Uno)" -#elif defined(IS_32U_FAMILY) - #pragma message "Microcontroller: ATmega32U4/16U4 (Leonardo/Pro Micro)" -#elif defined(IS_ATTINY_FAMILY) - #pragma message "Microcontroller: ATtiny85/45/25" +// Show detected microcontroller (e.g. __AVR_atmega328p__) +#define STRINGIZE(x) #x +#define TO_STR(x) STRINGIZE(x) + +#ifdef __AVR_DEVICE_NAME__ + #pragma message "Microcontroller: __AVR_" TO_STR(__AVR_DEVICE_NAME__) "__" +#endif + +// --- RESOURCE CONFLICT CHECK (ATtiny) --- +#if defined(IS_ATTINY_FAMILY) && \ + defined(LED_RUN) && \ + defined(PSNEE_DEBUG_SERIAL_MONITOR) + #error "Resource conflict: LED_RUN and DEBUG_SERIAL are incompatible on ATtiny." #endif // --- Feature Status --- @@ -430,7 +391,4 @@ void Debug_Inject(){ // Confirmation of region code injection #pragma message "Feature: Serial Debug Monitor ENABLED" #endif -#ifdef LED_RUN - #pragma message "Feature: Status LED (PB5) ENABLED" ->>>>>>> Stashed changes -#endif + diff --git a/images/test-mat b/images/test-mat.png similarity index 100% rename from images/test-mat rename to images/test-mat.png