diff --git a/PSNee/MCU.h b/PSNee/MCU.h new file mode 100644 index 0000000..96e85a4 --- /dev/null +++ b/PSNee/MCU.h @@ -0,0 +1,1032 @@ +// ***************************************************************************************************************** +// Configuration for different microcontrollers (MCU) to ensure compatibility with the code: +// - Defines the clock speed, timers, and interrupts for each MCU. +// - Configures I/O pins for data, clocks, and switches according to the requirements. +// - Enables pull-up resistors on input pins where needed. +// - Manages external interrupts and LED outputs for system feedback. +// - Ensures the setup is compatible with various microcontroller models (e.g., ATmega328, ATtiny series, etc.) +// ***************************************************************************************************************** + +//****************************************************************************************************************** +// Configuring the clock speed and associated registers. The formula for calculating +// the clock frequency is F_CPU / (TCCR0B |= (1< + #include + #include + #include + #include + #include + + + + // Main pin configuration + + // Define the main pins as inputs + #define PIN_DATA_INPUT DDRB &= ~(1 << DDB0) // Set DDRB register to configure PINB0 as input + #define PIN_WFCK_INPUT DDRB &= ~(1 << DDB1) // Set DDRB register to configure PINB1 as input + #define PIN_SQCK_INPUT DDRD &= ~(1 << DDD6) // Set DDRD register to configure PINB6 as input + #define PIN_SUBQ_INPUT DDRD &= ~(1 << DDD7) // Set DDRD register to configure PINB7 as input + + // 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 + + // 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 + + // 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 PINB1 is high (1) + + // --- 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 + + // --- BIOS Patching Configuration --- + #if defined(SCPH_102) || \ + defined(SCPH_100) || \ + defined(SCPH_7000_7500_9000) || \ + defined(SCPH_7000) || \ + defined(SCPH_3500_5000_5500) || \ + defined(SCPH_3000) || \ + defined(SCPH_1000) + + // 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 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))) + + // Hardware Interrupt (INT0) for AX pulse counting + #define PIN_AX_INTERRUPT_ENABLE EIMSK |= (1< + #include + #include + #include + #include + #include + + + + // --- 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) + + + + // Configure lines as outputs (for injection/override) + #define PIN_DATA_OUTPUT DDRB |= (1 << DDB4) + #define PIN_WFCK_OUTPUT DDRB |= (1 << DDB5) + + // 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 + + // 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) // LED on PB6 + #define PIN_LED_ON PORTB |= (1 << PB6) + #define PIN_LED_OFF PORTB &= ~(1 << PB6) + #endif + + // --- BIOS Patching Configuration (32U4 Mapping) --- + + #if defined(SCPH_102) || \ + defined(SCPH_100) || \ + defined(SCPH_7000_7500_9000) || \ + defined(SCPH_3500_5000_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) + #define PIN_AY_INTERRUPT_FALLING EICRA = (EICRA & ~((1 << ISC01) | (1 << ISC00))) | (1 << ISC01) // Configure INT1 for falling edge trigger + + #endif + + // Hardware Bypass Switch (On-the-fly deactivation) + #ifdef PATCH_SWITCHE + #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 + + + +#if defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny25__) + #define IS_ATTINY_FAMILY + + void OptimizePeripherals(void) __attribute__((naked, section(".init3"))); + + void OptimizePeripherals(void) { + + // Ultra-Fast Boot (Clock & Watchdog) + // Forced 8MHz is mandatory for stable SoftwareSerial baudrate + CLKPR = 0x80; + CLKPR = 0x00; + MCUSR = 0; + + //Global Interrupt Disable during reconfiguration + cli(); + + // Analog Modules Shutdown + ADCSRA = 0; // Power off ADC completely + ACSR |= (1 << ACD); // Disable Analog Comparator + + // 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) | + (0 << PRTIM0) | // KEEP TIMER 0 FOR SOFTWARE SERIAL + (1 << PRTIM1) | + (1 << PRUSI); + + // Timer 0 Specific Shutdown (Hardware Redundancy) + TCCR0B = 0; + TCCR0B = 0; + TIMSK = 0; // Disable ALL timer interrupts (OCIE0A, OCIE0B, TOIE0, etc.) + + // Watchdog: Ensure it's disabled to prevent random resets + MCUSR &= ~(1 << WDRF); + WDTCR |= (1 << WDCE) | (1 << WDE); + WDTCR = 0x00; + + } + + + + #include + #include + #include + #include + #include + #include + + // --- Main Bus Interface (CD-ROM Controller) --- + + // 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) + + // Configure lines as outputs (for injection/override) + #define PIN_DATA_OUTPUT DDRB |= (1 << DDB2) + #define PIN_WFCK_OUTPUT DDRB |= (1 << DDB4) + + // 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 + + // 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)) + + // --- Status Indication (LED) --- + #ifdef LED_RUN + #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(DEBUG_SERIAL_MONITOR) + #include + // 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) + #endif + + // --- Safety Check: BIOS Patch Compatibility --- + #if defined(SCPH_1000) || \ + defined(SCPH_3000) || \ + defined(SCPH_3500_5000_5500) || \ + defined(SCPH_7000_7500_9000) || \ + defined(SCPH_100) || \ + defined(SCPH_102) + #error "ATtiny85/45/25 architecture is not compatible with the BIOS patch feature." + #endif +#endif + +// ***************************************************************************************************************** +// WARNING: +// The following code is not functional as-is. +// ***************************************************************************************************************** + + +#ifdef ATtiny88_48 + + #define F_CPU 16000000L + #define TIMER_TCNT_CLEAR TCNT0 = 0x00 //TCNT0 - Timer/Counter Register + #define SET_OCROA_DIV OCR0A = 159; //OCR0A – Output Compare Register A, 0x10011111, 100KHz + #define SET_TIMER_TCCROA TCCR0A |= (1 << CTC0); //TCCR0A – Timer/Counter Control Register A. turn on CTC mode, CTC0 + // On ATtiny88, TCCR0B doesn't exist — clock select bits (CS01, CS00) are in TCCR0A. + // This sets the prescaler to 1, so the timer runs at full system clock (16 MHz). + #define SET_TIMER_TCCROB TCCR0A |= (1 << CS01) | (1 << CS00) + + #define CTC_TIMER_VECTOR TIMER0_COMPA_vect //interrupt vector for match event, OCR0A comparison and Timer/Counter 0 + + + #include + #include + #include + #include + #include + #include + + // Globale interrupt seting + #define GLOBAL_INTERRUPT_ENABLE SREG |= (1 << 7) + #define GLOBAL_INTERRUPT_DISABLE SREG &= ~(1 << 7) + + // Handling the main pins + + // Main pins input + #define PIN_DATA_INPUT DDRB &= ~(1 << DDB0) + #define PIN_WFCK_INPUT DDRB &= ~(1 << DDB1) // Create a mask (1<<0) with the first bit at 1 b00000001 uses the ~ operator to perform a bit inversion b11111110, + #define PIN_SQCK_INPUT DDRD &= ~(1 << DDD6) // &= updates the DDRB register with the AND operator and the mask, DDRB bxxxxxxxx OR mask b11111110 = bxxxxxxx0 + #define PIN_SUBQ_INPUT DDRD &= ~(1 << DDD7) + + // Main pin output + #define PIN_DATA_OUTPUT DDRB |= (1 << DDB0) // Create a mask (1<<0) with the first bit at 1 b00000001, + #define PIN_WFCK_OUTPUT DDRB |= (1 << DDB1) // |= updates the DDRB register with the OR operator and the mask, DDRB bxxxxxxxx OR mask b00000001 = bxxxxxxx1 + + // Define pull-ups and set high at the main pin + #define PIN_DATA_SET PORTB |= (1 << PB0) // Create a mask (1<<0) with the first bit at 1 b00000001, + // |= updates the PORTB register with the OR operator and the mask, PORTB bxxxxxxxx OR mask b00000001 = bxxxxxxx1 + + // Define pull-ups set down at the main pin + #define PIN_DATA_CLEAR PORTB &= ~(1 << PB0) // Create a mask (1<<0) with the first bit at 1 b00000001 uses the ~ operator to perform a bit inversion b11111110, + #define PIN_WFCK_CLEAR PORTB &= ~(1 << PB1) // &= updates the DDRB register with the AND operator and the mask, DDRB bxxxxxxxx OR mask b11111110 = bxxxxxxx0 + + // Read the main pins + #define PIN_SQCK_READ (PIND & (1 << PIND6)) // Create a mask (1<<6) with the six bit at 1 b00100000, + #define PIN_SUBQ_READ (PIND & (1 << PIND7)) // compare the PINB register and the mask with the AND operator, and returns the result, PINB bxx1xxxxx AND mask b00100000 = 1 + #define PIN_WFCK_READ (PINB & (1 << PINB1)) + + // Handling and use of the LED pin + #ifdef LED_RUN + #define PIN_LED_OUTPUT DDRD |= (1 << DDD0) + #define PIN_LED_ON PORTD |= (1 << PD0) + #define PIN_LED_OFF PORTD &= ~(1 << PD0) + #endif + + // Handling the BIOS patch + #if defined(SCPH_102) || defined(SCPH_100) || defined(SCPH_7000_9000) || defined(SCPH_5500) || defined(SCPH_3500_5000) || defined(SCPH_3000) || defined(SCPH_1000) + // BIOS interrupt seting + #define TIMER_INTERRUPT_ENABLE TIMSK0 |= (1 << OCIE0A) + #define TIMER_INTERRUPT_DISABLE TIMSK0 &= ~(1 << OCIE0A) + + // BIOS timer clear + #define TIMER_TIFR_CLEAR TIFR0 |= (1 << OCF0A) + + // Pins input + #define PIN_AX_INPUT DDRD &= ~(1 << DDD2) + #define PIN_AY_INPUT DDRD &= ~(1 << DDD3) + #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 << PIND2)) + #define PIN_AY_READ (PIND & (1 << PIND3)) + + // Handling the external interrupt + #define PIN_AX_INTERRUPT_ENABLE EIMSK |= (1 << INT0) + #define PIN_AY_INTERRUPT_ENABLE EIMSK |= (1 << INT1) + + #define PIN_AX_INTERRUPT_DISABLE EIMSK &= ~(1 << INT0) + #define PIN_AY_INTERRUPT_DISABLE EIMSK &= ~(1 << INT1) + + #define PIN_AX_INTERRUPT_RISING EICRA |= (1 << ISC01) | (1 << ISC00) + #define PIN_AY_INTERRUPT_RISING EICRA |= (1 << ISC11) | (1 << ISC10) + + #define PIN_AX_INTERRUPT_FALLING (EICRA = (EICRA & ~(1 << ISC00)) | (1 << ISC01)) + #define PIN_AY_INTERRUPT_FALLING (EICRA = (EICRA & ~(1 << ISC10)) | (1 << ISC11)) + + #define PIN_AX_INTERRUPT_VECTOR INT0_vect + #define PIN_AY_INTERRUPT_VECTOR INT1_vect + + // Handling and reading the switch pin for patch BIOS + #ifdef PATCH_SWITCH + #define PIN_SWITCH_INPUT DDRD &= ~(1 << DDD5) + #define PIN_SWITCH_SET PORTD |= (1 << PD5) + #define PIN_SWITCH_READ (PIND & (1 << PIND5)) + #endif + #endif + +#endif + +#ifdef ATtiny214_414 + +//#define SET_CTRLA +#define DF_CPU 20000000L +#define TIMER_TCNT_CLEAR TCA0.SINGLE.CNT = 0x00 //TCNT0 - Timer/Counter Register +#define SET_OCROA_DIV TCA0.SINGLE.CMP0L = 100; //OCR0A – Output Compare Register A, 0x10011111, 100KHz +#define SET_TIMER_TCCROA TCA0.SINGLE.CTRLB |= (1 << TCA_SINGLE_WGM0); //TCA_SINGLE_WGMODE_FRQ_gc //TCCR0A – Timer/Counter Control Register A. turn on CTC mode, CTC0 +#define SET_TIMER_TCCROB TCA0.SINGLE.CTRLA |= (1 << TCA_SINGLE_CLKSEL0); //TCA_SINGLE_CLKSEL_DIV1_gc//TCCR0B – Timer/Counter Control Register B, CS00: Clock Select, clk I/O + //Waveform Generation Mode, Mode 2 CTC +#define CTC_TIMER_VECTOR TCA0_OVF_vect //TCA0_CMP0_vect //interrupt vector for match event, OCR0A comparison and Timer/Counter 0 + + +#include +#include +#include +#include +#include +#include + +// Globale interrupt seting +#define GLOBAL_INTERRUPT_ENABLE __asm__ __volatile__("sei" ::) //CPU.SREG |= (1<<7) +#define GLOBAL_INTERRUPT_DISABLE __asm__ __volatile__("cli" ::) //CPU.SREG &= ~(1<<7) + +// Handling the main pins + +// Main pins input +#define PIN_DATA_INPUT PORTA.DIR = PIN2_bm +#define PIN_WFCK_INPUT PORTA.DIR = PIN1_bm // Create a mask (1<<0) with the first bit at 1 b00000001 uses the ~ operator to perform a bit inversion b11111110, +#define PIN_SQCK_INPUT PORTA.DIR = PIN4_bm // &= updates the DDRB register with the AND operator and the mask, DDRB bxxxxxxxx OR mask b11111110 = bxxxxxxx0 +#define PIN_SUBQ_INPUT PORTA.DIR = PIN3_bm + +// Main pin output +#define PIN_DATA_OUTPUT PORTA.DIR |= PIN2_bm // Create a mask (1<<0) with the first bit at 1 b00000001, +#define PIN_WFCK_OUTPUT PORTA.DIR |= PIN1_bm // |= updates the DDRB register with the OR operator and the mask, DDRB bxxxxxxxx OR mask b00000001 = bxxxxxxx1 + +// Define pull-ups and set high at the main pin +#define PIN_DATA_SET PORTA.OUT |= PIN2_bm // Create a mask (1<<0) with the first bit at 1 b00000001, + // |= updates the PORTB register with the OR operator and the mask, PORTB bxxxxxxxx OR mask b00000001 = bxxxxxxx1 + +// Define pull-ups set down at the main pin +#define PIN_DATA_CLEAR PORTA.OUT &= ~PIN2_bm // Create a mask (1<<0) with the first bit at 1 b00000001 uses the ~ operator to perform a bit inversion b11111110, +#define PIN_WFCK_CLEAR PORTA.OUT &= ~PIN1_bm // &= updates the DDRB register with the AND operator and the mask, DDRB bxxxxxxxx OR mask b11111110 = bxxxxxxx0 + +// Read the main pins +#define PIN_SQCK_READ PORTA.IN& PIN4_bm // Create a mask (1<<6) with the six bit at 1 b00100000, +#define PIN_SUBQ_READ PORTA.IN& PIN3_bm // compare the PINB register and the mask with the AND operator, and returns the result, PINB bxx1xxxxx AND mask b00100000 = 1 +#define PIN_WFCK_READ PORTA.IN& PIN1_bm + +// Handling and use of the LED pin +#define LED_RUN +#define PIN_LED_OUTPUT PORTB.DIR |= PIN2_bm +#define PIN_LED_ON PORTB.OUT |= PIN2_bm +#define PIN_LED_OFF PORTB.OUT &= ~PIN2_bm + +// Handling the BIOS patch + +// BIOS interrupt seting +#define TIMER_INTERRUPT_ENABLE TCA0.SPLIT.INTCTRL |= TCA_SINGLE_CMP0_bm +#define TIMER_INTERRUPT_DISABLE TCA0.SPLIT.INTCTRL &= ~TCA_SPLIT_HCMP0_bm + +// BIOS timer clear +#define TIMER_TIFR_CLEAR TCA0.SPLIT.INTFLAGS = TCA_SPLIT_HCMP0_bm + +// Pins input +#define PIN_AX_INPUT PORTB.DIR &= ~PIN3_bm +#define PIN_AY_INPUT PORTA.DIR &= ~PIN7_bm +#define PIN_DX_INPUT PORTA.DIR &= ~PIN6_bm +// Pin output +#define PIN_DX_OUTPUT PORTA.DIR |= PIN6_bm +// Define pull-ups set high +#define PIN_DX_SET PORTA.OUT |= PIN6_bm +// Define pull-ups set down +#define PIN_DX_CLEAR PORTA.OUT &= ~PIN6_bm +// Read pins for BIOS patch +#define PIN_AX_READ PORTB.IN& PIN3_bm +#define PIN_AY_READ PORTA.IN& PIN6_bm + +// Handling the external interrupt +//#define PIN_AX_INTERRUPT_ENABLE PORTB.PIN3CTRL |= (1< 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/PSNee.ino b/PSNee/PSNee.ino new file mode 100644 index 0000000..6db3deb --- /dev/null +++ b/PSNee/PSNee.ino @@ -0,0 +1,740 @@ + +// PSNee-V9.0 +/********************************************************************************************************************* + * CONSOLE MODEL SELECTION (SCPH Hardware Configuration) + *********************************************************************************************************************/ +/*-------------------------------------------------------------------------------------------------------------------- + * Models below do not require BIOS patching. + * Standard USB injection is supported. + * + * SCPH model number // region code | region + *--------------------------------------------------------------------------------------------------------------------*/ +// #define SCPH_xxx1 // NTSC U/C | America. +// #define SCPH_xxx2 // PAL | Europ. +// #define SCPH_xxx3 // NTSC J | Asia. +// #define SCPH_xxxx // Universal + +// #define SCPH_5903 // NTSC J | Asia VCD: + + +/*------------------------------------------------------------------------------------------------------------------ + * WARNING: These models REQUIRE a BIOS patch. + * + * ISP programming is MANDATORY. + * The Arduino bootloader introduces a delay that prevents the BIOS patch injection. + * Using an ISP programmer eliminates this delay. + * + * Note: BIOS version is more critical than the SCPH number for patch success. + *------------------------------------------------------------------------------------------------------------------- + * + * // Data pin | Adres pin | + * SCPH model number // | 32-pin BIOS | 40-pin BIOS | BIOS version + *-------------------------------------------------------------------------------------------------------------------*/ +// #define SCPH_102 // DX - D0 | AX - A7 | | 4.4e - CRC 0BAD7EA9, 4.5e -CRC 76B880E5 +// #define SCPH_100 // DX - D0 | AX - A7 | | 4.3j - CRC F2AF798B +// #define SCPH_7000_7500_9000 // DX - D0 | AX - A7 | | 4.0j - CRC EC541CD0 +// #define SCPH_3500_5000_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 + +/******************************************************************************************************************* + * Options + *******************************************************************************************************************/ + +#define REQUEST_INJECT_TRIGGER 10 // Now coupled with REQUEST_INJECT_GAP; allows for higher trigger +/* + * TRIGGER CALIBRATION: + * - Lower values (<5): Possible, but not beneficial. + * - The value of 11 for REQUEST_INJECT_TRIGGER must not be exceeded on Japanese models. + * This causes problems with disks that have anti-mod protection; it's less noticeable on other models. + * - Higher values (15-20-25-30): Possible for older or weak CD-ROM laser units. + */ + +#define REQUEST_INJECT_GAP 5 // Stealth interval (must be 4-8 AND < REQUEST_INJECT_TRIGGER) +/* + * NOTE: REQUEST_INJECT_GAP defines the "cool-off" period between injections. + * - Optimal range: 4 to 8 (for natural CD timing & anti-mod bypass). + * - Constraint: Must ALWAYS be lower than REQUEST_INJECT_TRIGGER. + */ + +#define LED_RUN // Enable visual feedback during injections. +/* + * LED CONNECTION GUIDE (1k ohm resistor recommended in series): + * - Arduino Uno/Nano: Uses onboard D13 LED. + * - ATtiny85/45: Connect LED between PB3 (Pin 2) and GND. + * - ATmega32U4 (Pro Micro): Connect LED between PB6 (Pin 10) and GND. + */ + +//#define PATCH_SWITCHE // This allows the user to disable the BIOS patch on-the-fly. +/* + * This allows you to bypass the memory card blocking problems on the SCPH-7000. + * - Configure Pin D5 as Input. + * - Enable internal Pull-up. + * - Exit immediately the patch BIOS if the switch pulls the pin to GND + */ + +// #define DEBUG_SERIAL_MONITOR // Enables serial monitor output. +/* + * Requires compilation with Arduino libs! + * For Arduino connect TXD and GND, for ATtiny PB3 (pin 2) and GND, to your serial card RXD and GND. + * + * For Arduino (Uno/Nano/Pro Mini): + * TX (Pin 1) -----> RX (Serial Card) + * GND -----> GND + * + * For ATtiny (25/45/85): + * Pin 2 (PB3) -----> RX (Serial Card) + * Pin 4 (GND) -----> GND + * + */ + +/****************************************************************************************************************** + * Summary of practical information. Fuses. Pinout + ******************************************************************************************************************* + * Fuses + * MCU | High | Low | Extended + * -------------------------------------------------- + * ATmega32U4 | DF | EE | D7 + * ATtiny | DF | E2 | FF + * + * Pinout + * Arduino | PSNee | + * --------------------------------------------------- + * VCC | VCC | + * GND | GND | + * RST | RESET | Only for JAP_FAT + * D2 | BIOS AX | Only for Bios patch + * D3 | BIOS AY | Only for BIOS patch SCPH_1000, SCPH_3000 + * D4 | BIOS DX | Only for Bios patch + * D5 | SWITCH | Optional for disabling Bios patch + * D6 | SQCK | + * D7 | SUBQ | + * D8 | DATA | + * D9 | WFCK | + * D13 ^ D10 | LED | D10 only for ATmega32U4 + * + * ATtiny | PSNee | ISP | + * --------------------------------------------------- + * Pin1 | | RESET | + * Pin2 | LED ^ serial | | serial only for DEBUG_SERIAL_MONITOR + * Pin3 | WFCK | | + * Pin4 | GND | GND | + * Pin5 | SQCK | MOSI | + * Pin6 | SUBQ | MISO | + * Pin7 | DATA | SCK | + * Pin8 | VCC | VCC | + *******************************************************************************************************************/ + +/******************************************************************************************************************* + * pointer and variable section + *******************************************************************************************************************/ + +#include "MCU.h" +#include "settings.h" + +uint8_t wfck_mode = 0; //Flag initializing for automatic console generation selection 0 = old, 1 = pu-22 end ++ +uint8_t SUBQBuffer[12]; // Global buffer to store the 12-byte SUBQ channel data + +uint8_t request_counter = 0; + +#if defined(DEBUG_SERIAL_MONITOR) + uint16_t global_window = 0; // Stores the remaining cycles from the detection window +#endif + +/******************************************************************************************************************* + * Code section + ********************************************************************************************************************/ +/**************************************************************************************** + * FUNCTION : Bios_Patching() + * + * OPERATION : Real-time Data Bus (DX) override via Address Bus (AX / AY) + * + * KEY PHASES: + * 1. STABILIZATION & ALIGNMENT (AX): + * Synchronizes the execution pointer with the AX rising edge to establish + * a deterministic hardware timing reference. + * + * 2. SILENCE DETECTION (BOOT STAGE): + * Validates consecutive silent windows (SILENCE_THRESHOLD) to identify + * the specific boot stage before the target address call. + * + * 3. HARDWARE COUNTING & OVERDRIVE (AX): + * Engages INT0 to count AX pulses. On the final pulse, triggers a + * bit-aligned delay to force a custom state on the DX line. + * + * 4. SECONDARY SILENCE (GAP DETECTION): + * If PHASE_TWO_PATCH is active, monitors for a secondary silent gap + * (CONFIRM_COUNTER_TARGET_2) between patching windows. + * + * 5. SECONDARY OVERDRIVE (AY): + * Engages INT1 (AY) for the final injection stage, synchronizing the + * patch with the secondary memory address cycles. + * + * CRITICAL TIMING & TIMER-LESS ALIGNMENT: + * - DETERMINISTIC SILENCE: Uses cycle-accurate polling to filter boot jitter + * and PS1 initialization noise, replacing unstable hardware timers. + * + * - CYCLE STABILIZATION (16MHz LIMIT): Uses '__builtin_avr_delay_cycles' to + * prevent compiler reordering. At 16MHz, the CPU has zero margin; a single + * instruction displacement would break high-speed bus alignment. + * + **************************************************************************************/ + + +#ifdef BIOS_PATCH + + /** + * 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) + + /** + * 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); + + #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 + } + } + + #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); + + 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 --- + #if defined(PATCH_SWITCHE) + 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 + + 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 + + // --- PHASE 1: STABILIZATION & ALIGNMENT (AX) --- + // Establish a deterministic hardware timing reference. + 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 + } + + // --- 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 + } + } + + // --- 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 + + 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 + + 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 + +/******************************************************************************************************************* + * FUNCTION : BoardDetection + * + * DESCRIPTION : + * Distinguishes motherboard generations (PU-7 through PU-18) 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. + * - 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. + * + *******************************************************************************************************************/ + +void BoardDetection() { + wfck_mode = 0; // Default: Legacy (GATE) + uint8_t pulse_hits = 25; // We need to see 25 oscillations to confirm FREQUENCY mode + uint16_t detectionWindow = 10000; + _delay_ms(300); // Wait for WFCK to stabilize (High on Legacy, Oscillation on Modern) + + while (--detectionWindow) { + /** + * LOGIC BASED ON YOUR ANALYSIS: + * If WFCK is "CONTINUOUS" (Legacy), it stays HIGH. PIN_WFCK_READ will always be 1. + * If WFCK is "FREQUENCY" (Modern), it will hit 0 (LOW) periodically. + */ + if (!PIN_WFCK_READ) { // Detect a LOW state (only possible in FREQUENCY mode) + + pulse_hits--; // Record one oscillation hit + + if (pulse_hits == 0) { + wfck_mode = 1; // Confirmed: FREQUENCY mode (PU-22 or newer) + #if defined(DEBUG_SERIAL_MONITOR) + global_window = detectionWindow; + #endif + return; // Exit as soon as we are sure + } + + /** + * SYNC: Wait for the signal to go HIGH again. + * This ensures we count each pulse of the "FREQUENCY" signal only once. + */ + while (!PIN_WFCK_READ && detectionWindow > 0) { + detectionWindow--; + } + } + } + // If the window expires without seeing enough LOW pulses, it remains wfck_mode = 0 (GATE) +} + +/****************************************************************************************************************** + * FUNCTION : CaptureSUBQ + * + * DESCRIPTION : + * Captures a complete 12-byte SUBQ frame. + * Synchronizes with the SQCK (SUBQ Clock) pin to shift in serial data. + * Implementation: Synchronous Serial, LSB first reconstruction. + ******************************************************************************************************************/ + + +void CaptureSUBQ(void) { + uint8_t bytesRemaining = 12; // Total frame size for a complete SUBQ + uint8_t* bufferPtr = SUBQBuffer; + + do { + 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 + + /** + * 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; + } + } + // Store reconstructed byte and advance pointer + *bufferPtr++ = currentByte; + + } while (--bytesRemaining); // Efficient countdown for AVR binary size + + #if defined(DEBUG_SERIAL_MONITOR) + CaptureSUBQLog(SUBQBuffer); + #endif +} + +/****************************************************************************************** + * FUNCTION : Filter_SUBQ_Samples() [SCPH_5903 Dual-Interface Variant] + * + * 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 'request_counter' 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 controlByte) { + + // --- STEP 0: Data/TOC Validation --- + // Optimized mask (0xD0) to verify bit6 is SET while bit7 and bit4 are CLEARED. + uint8_t isDataSector = ((controlByte & 0xD0) == 0x40); + + // --- STEP 1: SUBQ Frame Synchronization --- + // Fast filtering: ignore raw data if sync markers (index 1 and 6) are not 0x00. + if (SUBQBuffer[1] == 0x00 && SUBQBuffer[6] == 0x00) { + + /** + * HIT INCREMENT CONDITIONS: + * A. VALID PSX LEAD-IN: Data sector AND Point A0-A2 range AND NOT VCD (sub-mode != 0x02). + * (uint8_t)(SUBQBuffer[2] - 0xA0) <= 2 is an optimized check for 0xA0, 0xA1, 0xA2. + * B. TRACKING MAINTENANCE: Keeps count if already synced and reading Mode 0x01 or Data. + */ + if ( (isDataSector && (uint8_t)(SUBQBuffer[2] - 0xA0) <= 2 && SUBQBuffer[3] != 0x02) || + (request_counter > 0 && (SUBQBuffer[0] == 0x01 || isDataSector)) ) + { + request_counter++; // Direct increment: faster on 16MHz AVR + return; + } + } + + // --- STEP 2: Signal Decay / Pattern Mismatch --- + // Decrement the hit counter if no valid PSX pattern is detected in the SUBQ stream. + if (request_counter > 0) { + request_counter--; // Direct decrement: saves CPU cycles + } + } +#else + +/****************************************************************************************** + * FUNCTION : FilterSUBQSamples() + * + * DESCRIPTION : + * Parses and filters the raw serial data stream from the SUBQ pin. + * Increments a hit counter (request_counter) 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 controlByte) { + + // --- STEP 0: Data/TOC Validation --- + // Optimized mask (0xD0) to verify bit6 is SET while bit7 and bit4 are CLEARED. + uint8_t isDataSector = ((controlByte & 0xD0) == 0x40); + + // --- STEP 1: SUBQ Frame Synchronization --- + // Ignore the raw bitstream unless sync markers (1 & 6) are 0x00. + if (SUBQBuffer[1] == 0x00 && SUBQBuffer[6] == 0x00) { + + /* + * HIT INCREMENT CONDITIONS: + * A. LEAD-IN PATTERNS: Detects TOC markers (A0-A2) or Track 01 at the spiral start. + * The calculation (uint8_t)(SUBQBuffer[3] - 0x03) >= 0xF5 handles + * the 0x98 to 0x02 wrap-around near the TOC boundary. + * B. TRACKING LOCK: Maintains the counter if already synced and reading + * valid sectors (Audio or Data). + */ + + if ( (isDataSector && (SUBQBuffer[2] >= 0xA0 || (SUBQBuffer[2] == 0x01 && ( (uint8_t)(SUBQBuffer[3] - 0x03) >= 0xF5)))) || + (request_counter > 0 && (SUBQBuffer[0] == 0x01 || isDataSector)) ) + { + request_counter++; // Direct increment: saves CPU cycles + return; + } + } + + // --- STEP 2: Signal Decay / Missed Hits --- + // Reduce the hit counter if the current SUBQ sample fails validation. + if (request_counter > 0) { + request_counter--; // Direct decrement: faster on 16MHz AVR + } + } + +#endif +/********************************************************************************************* +* FUNCTION : PerformInjectionSequence + * + * DESCRIPTION : + * Executes the SCEx injection sequence to bypass the CD-ROM regional lockout. + * Supports two hardware-specific injection methods: + * + * 1. Legacy Gate Mode (PU-7 to PU-20): Modchip acts as a logic gate to pull + * the signal down. WFCK is simulated by the chip if necessary. + * + * 2. WFCK SYNC MODE (PU-22 and later): + * Synchronizes data injection with the console's hardware WFCK clock. + * The signal is modulated on every clock edge to ensure + * alignment with the CD-ROM controller's internal timing. + * + * 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) { + static const uint8_t allRegionsSCEx[3][6] = { + { 0x59, 0xC9, 0x4B, 0x5D, 0xDA, 0x02 }, // SCEI (Jap) + { 0x59, 0xC9, 0x4B, 0x5D, 0xFA, 0x02 }, // SCEA (USA) + { 0x59, 0xC9, 0x4B, 0x5D, 0xEA, 0x02 } // SCEE (PAL) + }; + + #ifdef LED_RUN + PIN_LED_ON; + #endif + + const uint16_t BIT_DELAY = 4000; + + PIN_DATA_OUTPUT; + PIN_DATA_CLEAR; + + if (!wfck_mode) { // Legacy timing mode + PIN_WFCK_OUTPUT; + PIN_WFCK_CLEAR; + } + + for (uint8_t regionCycle = 0; regionCycle < 3; regionCycle++) { + // Select the current region index to inject (Use cycle index if Universal mode 3) + uint8_t regionIndex = (injectSCEx == 3) ? regionCycle : injectSCEx; + + + // 44-bit Loop (LSB first) + for (uint8_t bitPosition = 0; bitPosition < 44; bitPosition++) { + // Direct bit extraction from the array (Index >> 3 = Byte, Index & 0x07 = Bit) + uint8_t currentBit = (allRegionsSCEx[regionIndex][bitPosition >> 3] >> (bitPosition & 0x07)) & 0x01; + + if (wfck_mode) { + /* METHOD 1: PULSE COUNTING (WFCK SYNC) */ + for (uint8_t count = 30; count > 0; count--) { + // Wait for falling edge (HIGH to LOW) + while (PIN_WFCK_READ); + PIN_DATA_CLEAR; // Pulse start + + // Wait for rising edge (LOW to HIGH) + while (!PIN_WFCK_READ); + if (currentBit) { + PIN_DATA_SET; // Modulate if Bit is 1 + } + } + } else { + /* METHOD 2: TIME REFERENCE (FIXED DELAY) */ + if (currentBit == 0) { + PIN_DATA_CLEAR; + PIN_DATA_OUTPUT; + } else { + // High-Z mode (Input) for Bit 1 + PIN_DATA_INPUT; + } + _delay_us(BIT_DELAY); + } + } + + // EXIT CONDITION: Stop after the first successful region unless Universal mode (3) + if (injectSCEx != 3) { + PIN_DATA_OUTPUT; + PIN_DATA_CLEAR; + + if (!wfck_mode) // Set WFCK pin input + { + PIN_WFCK_INPUT; + } + break; // Stop immediately after the first region injection + } + + // Inter-region delay for Universel (3) + PIN_DATA_OUTPUT; + PIN_DATA_CLEAR; + _delay_ms(90); + } + + + if (!wfck_mode) // Set WFCK pin input + { + PIN_WFCK_INPUT; + } + + #ifdef LED_RUN + PIN_LED_OFF; + #endif + + + #if defined(DEBUG_SERIAL_MONITOR) + InjectLog(); + #endif +} + +void Init() { + + #ifdef LED_RUN + PIN_LED_OUTPUT; + #endif + + // --- Critical Boot Patching --- + #ifdef BIOS_PATCH + +// #ifdef LED_RUN +// PIN_LED_ON; +// #endif + + // Execute BIOS patching + Bios_Patching(); +// #ifdef LED_RUN +// PIN_LED_OFF; +// #endif + #endif + + PIN_SQCK_INPUT; + PIN_SUBQ_INPUT; + + // --- Debug Interface Setup --- + #if defined(DEBUG_SERIAL_MONITOR) && defined(IS_ATTINY_FAMILY) + pinMode(debugtx, OUTPUT); // software serial tx pin + mySerial.begin(115200); // 13,82 bytes in 12ms, max for softwareserial + #elif defined(DEBUG_SERIAL_MONITOR) && defined(IS_32U4_FAMILY) + // On 32U4, Serial1 is the hardware UART on pins D0 (RX) and D1 (TX) + Serial1.begin(500000); + #elif defined(DEBUG_SERIAL_MONITOR) && defined(IS_328_168_FAMILY) + Serial.begin(500000); // Standard hardware UART for 328/168 + #endif + + // Identify board revision (PU-7 to PU-22+) to set correct injection timings + BoardDetection(); +} + +int main() { + + Init(); + + #if defined(DEBUG_SERIAL_MONITOR) + // Display initial board detection results (Window remaining & WFCK mode) + BoardDetectionLog( global_window, wfck_mode, INJECT_SCEx); + #endif + + while (1) { + + _delay_ms(1); // Timing Sync: Prevent reading the tail end of the previous SUBQ packet + + CaptureSUBQ(); // DATA ACQUISITION: Capture the 12-byte SUBQ stream. + + /** + * REQUEST FILTERING: Analyze the SUBQ Control byte (SUBQBuffer[0]). + * If valid, 'request_counter' is incremented inside the filter. + * Masking (0xD0) and pattern matching are handled internally. + */ + FilterSUBQSamples(SUBQBuffer[0]); + + /** + * INJECTION TRIGGER: Once enough valid requests are detected, + * trigger the SCEx injection sequence. + */ + if (request_counter >= REQUEST_INJECT_TRIGGER) { + + PerformInjectionSequence(INJECT_SCEx); // Execute the SCEx injection burst + + // STEALTH GAP: Mimics natural CD behavior to bypass anti-mod detection. + request_counter = (REQUEST_INJECT_TRIGGER - REQUEST_INJECT_GAP); + } + } + return 0; +} diff --git a/PSNee/settings.h b/PSNee/settings.h new file mode 100644 index 0000000..054cb8a --- /dev/null +++ b/PSNee/settings.h @@ -0,0 +1,333 @@ +#pragma once + +/* + * + * + */ + +/*------------------------------------------------------------------------------------------------ + Specific parameter section for BIOS patches +------------------------------------------------------------------------------------------------*/ + + // Results of the maximum values + // tested with an Atmega328P + +#if defined(IS_32U4_FAMILY) + + // ------ SCPH 100 / 102 ------ + #if defined(SCPH_100) || \ + defined(SCPH_102) + #define BIOS_PATCH + #define SILENCE_THRESHOLD 1100 + #define CONFIRM_COUNTER_TARGET 8 + #define PULSE_COUNT 47 + #define BIT_OFFSET_CYCLES 47 + #define OVERRIDE_CYCLES 3 + #endif + + + // // -------- SCPH 7000 / 7500 / 9000 -------- + #ifdef SCPH_7000_7500_9000 + #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_5000_5500 + #define BIOS_PATCH + #define SILENCE_THRESHOLD 25000 + #define CONFIRM_COUNTER_TARGET 1 + #define PULSE_COUNT 84 + #define BIT_OFFSET_CYCLES 47 + #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 4 + #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 43 + #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 + +#if defined(IS_328_168_FAMILY) + + // ------ 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 + #define BIT_OFFSET_CYCLES 47 + #define OVERRIDE_CYCLES 3 + #endif + + + // // -------- SCPH 7000 / 7500 / 9000 -------- + #ifdef SCPH_7000_7500_9000 + #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 + + // // ----- SCPH 3500 / 5000 / 5500 ----- + #ifdef SCPH_3500_5000_5500 + #define BIOS_PATCH + #define SILENCE_THRESHOLD 32000 + #define CONFIRM_COUNTER_TARGET 1 + #define PULSE_COUNT 84 + #define BIT_OFFSET_CYCLES 47 + #define OVERRIDE_CYCLES 3 + #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 + + + // // -------- 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_7000_7500_9000) || \ + defined(SCPH_3500_5000_5500) || \ + defined(SCPH_3000) || \ + defined(SCPH_1000) || \ + defined(SCPH_xxx3) || \ + defined(SCPH_5903) + + #define INJECT_SCEx 0 // NTSC-J + +#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 + +#endif + +/*------------------------------------------------------------------------------------------------ + serial debug section +------------------------------------------------------------------------------------------------*/ + +#if defined(DEBUG_SERIAL_MONITOR) +extern uint8_t request_counter; + + #if defined(IS_32U4_FAMILY) + #define DEBUG_OUT Serial1 + #else + #define DEBUG_OUT Serial + #endif + +void BoardDetectionLog (uint16_t window_result, uint8_t Wfck_mode, uint8_t region){ //Information about the MCU, and old or late console mode. + +#if defined(IS_ATTINY_FAMILY) + mySerial.print("m "); mySerial.println(Wfck_mode); +#else + static const char* regionNames[] = { + "NTSC-J", // 0 + "NTSC-U/C", // 1 + "PAL", // 2 + "Universal" // 3 + }; + + DEBUG_OUT.println(""); + DEBUG_OUT.print(F(" CPU Speed: ")); DEBUG_OUT.print(F_CPU / 1000000L); DEBUG_OUT.println(F(" MHz")); + DEBUG_OUT.print(F(" Sync Window: ")); DEBUG_OUT.println(window_result); // Real-time diagnostic + DEBUG_OUT.print(F(" WFCK Mode: ")); DEBUG_OUT.println(Wfck_mode); + DEBUG_OUT.print(F(" Region ID: ")); DEBUG_OUT.println(regionNames[region]); + DEBUG_OUT.println(""); +#endif + +} + + +void CaptureSUBQLog(uint8_t *dataBuffer) { + static uint16_t errorCount = 0; // Tracks missed sectors between valid reads + + // Cache the first 4 bytes for a quick null check (failed read) + uint8_t b0 = dataBuffer[0], b1 = dataBuffer[1], b2 = dataBuffer[2], b3 = dataBuffer[3]; + + // --- ERROR FILTERING --- + // If the sector is empty (all zeros), increment error counter and exit + if (b0 == 0 && b1 == 0 && b2 == 0 && b3 == 0) { + errorCount++; + return; + } + + + // --- DATA & request_counter DISPLAY --- + #if defined(IS_ATTINY_FAMILY) + // Compact hex output for ATtiny to maintain 12ms timing + for (uint8_t i = 0; i < 12; i++) { + uint8_t val = dataBuffer[i]; + if (val < 0x10) mySerial.print("0"); + mySerial.print(val, HEX); + } + // Append global request_counter on the same line + mySerial.print(F(" h:")); + mySerial.println(request_counter); + #else + // Formatted hex output for ATmega + for (uint8_t i = 0; i < 12; i++) { + uint8_t val = dataBuffer[i]; + if (val < 0x10) DEBUG_OUT.print("0"); + DEBUG_OUT.print(val, HEX); + DEBUG_OUT.print(" "); + } + // Append global request_counter on the same line + DEBUG_OUT.print(F("| Hyst: ")); + DEBUG_OUT.println(request_counter); + #endif +} + + + +void InjectLog(){ + + #if defined(IS_ATTINY_FAMILY) + mySerial.print("!"); // --- MINIMALIST NOTIFICATION (ATtiny) --- + #else + DEBUG_OUT.println(" INJECT ! "); + #endif +} + +#endif + +/*------------------------------------------------------------------------------------------------ + Compilation message +-----------------------------------------------------------------------------------------------*/ +#if (REQUEST_INJECT_GAP >= REQUEST_INJECT_TRIGGER) + #error "Critical Error: REQUEST_INJECT_GAP must be smaller than REQUEST_INJECT_TRIGGER!" +#endif + +#ifdef LED_RUN + #pragma message "Feature: Status LED ENABLED" +#endif + +// 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_5000_5500) + \ + defined(SCPH_7000_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 + +// Show target console. +#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_5000_5500) + #pragma message "Target Console: SCPH-3500/5000/5500 (NTSC-J)" +#elif defined(SCPH_7000_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_102) + #pragma message "Target Console: SCPH-102 (PAL)" +#elif defined(SCPH_xxx1) + #pragma message "Target Console: SCPH_xxx1 Generic NTSC-U/C" +#elif defined(SCPH_xxx2) + #pragma message "Target Console: SCPH_xxx2 Generic PAL" +#elif defined(SCPH_xxx3) + #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: SCPH_xxxx Universal Region Mode" +#else + // Error if no console is uncommented + #error "Console not selected! Please uncomment one SCPH model." +#endif + +// 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(DEBUG_SERIAL_MONITOR) + #error "Resource conflict: LED_RUN and DEBUG_SERIAL are incompatible on ATtiny." +#endif + +// --- Feature Status --- + +#ifdef DEBUG_SERIAL_MONITOR + #pragma message "Feature: Serial Debug Monitor ENABLED" +#endif + + diff --git a/README.md b/README.md index 78e03a1..ee3dd28 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,41 @@ -# PSNee V8.6 -THE modechip supports the largest number of Playstation 1 variants, and the largest number of microcontrollers -# For all useful information consult the ![Wiki](https://github.com/kalymos/PsNee/wiki) +# PSNee V9.0 +PSNee is compatible with all retail PlayStation 1 models and various AVR microcontrollers. -![Logo](/images/PSNee-8.6-logo.png) +## For all useful information, please consult the [Wiki](https://github.com). + +![Logo](/images/PSNee-9.0-logo.png) ## Features -- Remove Disk Region Protection -- Patch BIOS -- A specific library for card support, to solve the fuse setting problem. -- The mode does not take care of changing PAL <-> NTSC video output (in other words if you use a Japanese console and you put European games, or in older European models you use American or Japanese games... the display will not be correct) +- **Disc Region Protection:** Removes region locks. +- **BIOS Patching:** Provides enhanced compatibility. +- **Board Support:** Custom boards manager to simplify fuse settings and hardware configuration. -## Supported Playstation 1 -All US models, all European models, and the vast majority of Japanese models. +> [!IMPORTANT] +> **Video Signal Note:** This modchip does not handle PAL/NTSC video signal conversion. Playing games from a different region may result in an incorrect display (e.g., NTSC games on early PAL consoles, or PAL games on NTSC-U/C or NTSC-J consoles). +> +> European models from SCPH-5502 to SCPH-9002 typically handle NTSC signals correctly. However, **SCPH-102** requires a BIOS patch to support it. +> +> See the Example of a distorted image at the bottom of the page. -## Supported platforms -- ATmega328(A/P/PA) @16Mhz -- ATmega168(A/P/PA) @16Mhz -- Atmega32U4 @16Mhz -- ATtiny25/45/85 @8Mhz no BIOS patch! +## Supported Hardware (AVR) +- **ATmega328(A/P/PA)** @16MHz +- **ATmega168(A/P/PA)** @16MHz +- **ATmega32U4** @16MHz +- **ATtiny25/45/85** @8MHz *(No BIOS patch support!)* + +> [!CAUTION] +> **SCPH-7000 NTSF J (BIOS Patch & Memory Cards):** +> On SCPH-7000 models, the BIOS patch has an issue with memory cards. The only way to bypass it is to install a switch on the mod to disable the BIOS patch. This is done according to the available diagram. + +--- + +### Example of a distorted image +*Typical result when playing a game from a different region without a signal converter.* +![gray](images/issue/gray-screens.png) + +### Verified Hardware (Personal Test Collection) +![test](images/test-mat.png) +*From left to right: NTSC-J, PAL, and NTSC-U/C & Asia models.* + +[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/B0B81WGP0Z) -## Model that I personally tested -![test](images/test-PSNee-v8.6.png) -Example of gray image -![gray](https://github.com/kalymos/PsNee/blob/master/images/issue/gray-screens.png) diff --git a/changelog b/changelog index 3329036..c2c25af 100644 --- a/changelog +++ b/changelog @@ -1,8 +1,24 @@ +Update: April 19, 2026 + +- Total code refactor, division into functions. +- Elimination of the patchbios file. +- MCU optimization improvements. +- Bios_Patching: function improvement by eliminating the initial timer and replacing it with a wait for silence on the AX pin; + elimination of timers in ISRs by replacing them with NOPs. +- BoardDetection: function optimization. +- FilterSUBQSamples: logic optimization. +- PerformInjectionSequence: logic optimization and addition of a mode for synchronization with the WFCK clock. +- Init & Main: code cleanup. + +------------------------------------------------------------- + Update:mars 2025 -Added led support for ATtiny -Added card support to simplify fuse management +------------------------------------------------------------- + Update:july 2024 -Reimplementation of support for MUCs ATtiny25/45/85, Atmega32u4 diff --git a/images/BIOS/32p SCPH 1000-3000-pic.png b/images/BIOS/32p SCPH 1000-3000-pic.png deleted file mode 100644 index f1c889a..0000000 Binary files a/images/BIOS/32p SCPH 1000-3000-pic.png and /dev/null differ diff --git a/images/BIOS/32p SCPH 3500-5000-pic.png b/images/BIOS/32p SCPH 3500-5000-pic.png deleted file mode 100644 index b9f4457..0000000 Binary files a/images/BIOS/32p SCPH 3500-5000-pic.png and /dev/null differ diff --git a/images/BIOS/32p SCPH 3500->5500-pic.png b/images/BIOS/32p SCPH 3500->5500-pic.png index 641ac6e..360e8f0 100644 Binary files a/images/BIOS/32p SCPH 3500->5500-pic.png and b/images/BIOS/32p SCPH 3500->5500-pic.png differ diff --git a/images/BIOS/32p SCPH 7000->100-pic.png b/images/BIOS/32p SCPH 7000->100-pic.png index 17825de..e5a68ea 100644 Binary files a/images/BIOS/32p SCPH 7000->100-pic.png and b/images/BIOS/32p SCPH 7000->100-pic.png differ diff --git a/images/BIOS/32p.png b/images/BIOS/32p.png new file mode 100644 index 0000000..41a4dff Binary files /dev/null and b/images/BIOS/32p.png differ diff --git a/images/BIOS/40p SCPH 1000-3000-pic.png b/images/BIOS/40p SCPH 1000-3000-pic.png index 005db75..f2d03a8 100644 Binary files a/images/BIOS/40p SCPH 1000-3000-pic.png and b/images/BIOS/40p SCPH 1000-3000-pic.png differ diff --git a/images/BIOS/40p SCPH 3500-5000-pic.png b/images/BIOS/40p SCPH 3500-5000-pic.png index a74fb8c..a31608b 100644 Binary files a/images/BIOS/40p SCPH 3500-5000-pic.png and b/images/BIOS/40p SCPH 3500-5000-pic.png differ diff --git a/images/BIOS/40p.png b/images/BIOS/40p.png new file mode 100644 index 0000000..8923f81 Binary files /dev/null and b/images/BIOS/40p.png differ diff --git a/images/MUC Arduino/PSNee_V8_pinout.png b/images/MUC Arduino/PSNee_V9_pinout.png similarity index 100% rename from images/MUC Arduino/PSNee_V8_pinout.png rename to images/MUC Arduino/PSNee_V9_pinout.png diff --git a/images/PSNee-8.6-logo.png b/images/PSNee-8.6-logo.png deleted file mode 100644 index a7b663c..0000000 Binary files a/images/PSNee-8.6-logo.png and /dev/null differ diff --git a/images/PSNee-9.0-logo.png b/images/PSNee-9.0-logo.png new file mode 100644 index 0000000..1bd841d Binary files /dev/null and b/images/PSNee-9.0-logo.png differ diff --git a/images/my test material/test console.jpg b/images/my test material/test console.jpg deleted file mode 100644 index 33dd5be..0000000 Binary files a/images/my test material/test console.jpg and /dev/null differ diff --git a/images/my test material/test-mat.png b/images/my test material/test-mat.png new file mode 100644 index 0000000..1a7e473 Binary files /dev/null and b/images/my test material/test-mat.png differ diff --git a/images/test-PSNee-v8.6.png b/images/test-PSNee-v8.6.png deleted file mode 100644 index a5c8776..0000000 Binary files a/images/test-PSNee-v8.6.png and /dev/null differ diff --git a/images/tuto/blink-ISP.gif b/images/tuto/blink-ISP.gif index a2d5bd8..03953c8 100644 Binary files a/images/tuto/blink-ISP.gif and b/images/tuto/blink-ISP.gif differ diff --git a/images/tuto/blink-USB.gif b/images/tuto/blink-USB.gif index 0fd9130..39adc72 100644 Binary files a/images/tuto/blink-USB.gif and b/images/tuto/blink-USB.gif differ