1
0
mirror of https://github.com/kalymos/PsNee.git synced 2026-05-08 16:32:17 +00:00

Commit: System Optimization & Unified Architecture

Unified BIOS Patching: Merged management for SCPH-5500, 5000, and 3500.

New Hardware Bypass: Added a physical switch option for SCPH-7000 to disable the BIOS patch on-the-fly.

Compilation Feedback: Integrated #pragma messages to display during the build process.

Code Documentation: improved technical clarity.
This commit is contained in:
kalymos
2026-03-16 19:52:12 +01:00
parent 10a054d60c
commit 465d22166d
4 changed files with 688 additions and 424 deletions

View File

@@ -11,21 +11,21 @@
* delays to inject modified data onto the Data line (DX) in real-time.
*
* KEY PHASES:
* 1. STABILIZATION & ALIGNMENT (AX): Synchronizes execution with a clean rising
* 1. STABILIZATION & ALIGNMENT (AX): Synchronizes execution with a clean rising
* edge of the AX signal to establish a deterministic timing reference.
*
* 2. SILENCE DETECTION (GATING): Scans for a specific window of bus inactivity
* (SILENCE_THRESHOLD) to ensure the system is at the correct pre-patching stage.
* 2. ADDRESS CALL DETECTION (PHASE 2): Scans the bus for specific address calls
* by validating consecutive polling blocks (SILENCE_THRESHOLD).
*
* 3. HARDWARE PULSE COUNTING (ISR): Switches to interrupt-driven logic (INT0/INT1)
* to count address pulses with minimal latency and high-priority execution.
* 3. SILENCE CONFIRMATION (GATING): Counts the exact number of validated silence
* windows (CONFIRM_COUNTER_TARGET) to reach the correct pre-patching stage.
*
* 4. DATA OVERDRIVE (DX): Upon reaching the target pulse, triggers a calibrated
* delay (BIT_OFFSET) and momentarily forces the DX pin to OUTPUT mode to
* 4. HARDWARE PULSE COUNTING (ISR): Uses INT0/INT1 to decrement the 'impulse'
* counter on each rising edge with minimal hardware latency.
*
* 5. DATA OVERDRIVE (DX): Upon reaching the target pulse, triggers a calibrated
* delay (BIT_OFFSET) and momentarily forces the DX pin to OUTPUT mode to
* overwrite the BIOS bit for a precise duration (OVERRIDE).
*
* 5. SECONDARY PATCH (AY): If enabled, repeats the silence/counting sequence
* on a secondary address line (AY) for multi-stage memory patching.
* ======================================================================================
*/
@@ -35,12 +35,12 @@
* Shared state variables between ISRs and the main patching loop.
* Declared 'volatile' to prevent compiler optimization during busy-wait loops.
*/
volatile uint8_t impulse = 0; // Current address pulse countdown
volatile uint8_t impulse = 0; // Down-counter for physical address pulses
volatile uint8_t patch = 0; // Synchronization flag (0: Idle, 1: AX Done, 2: AY Done)
/**
* PHASE 3: Primary Interrupt Service Routine (AX)
* Executes the real-time override on the target address cycle.
* Triggered on rising edges to perform the real-time bus override.
*/
ISR(PIN_AX_INTERRUPT_VECTOR) {
if (--impulse == 0) {
@@ -89,10 +89,26 @@ ISR(PIN_AY_INTERRUPT_VECTOR) {
#endif
void Bios_Patching(void) {
uint8_t current_confirms = 0;
uint16_t count;
patch = 0;
// --- HARDWARE BYPASS OPTION (SCPH-7000 specific) ---
#if defined(SCPH_7000)
PIN_SWITCH_INPUT; // Configure Pin D5 as Input
PIN_SWITCH_SET; // Enable internal Pull-up (D5 defaults to HIGH)
__builtin_avr_delay_cycles(2); // Short delay for voltage stabilization
/**
* Exit immediately if the switch pulls the pin to GND (Logic LOW).
* This allows the user to disable the BIOS patch on-the-fly.
*/
if (PIN_SWITCH_READ == 0) {
return;
}
#endif
uint8_t current_confirms = 0;
//uint16_t count;
patch = 0; // Reset sync flag
sei(); // Enable Global Interrupts
PIN_AX_INPUT; // Set AX to monitor mode
@@ -106,9 +122,9 @@ void Bios_Patching(void) {
}
// --- PHASE 2: SILENCE DETECTION ---
// Accumulate validated silence blocks to filter boot noise.
// Validate the exact number of silence windows to identify the boot stage.
while (current_confirms < CONFIRM_COUNTER_TARGET) {
count = SILENCE_THRESHOLD;
uint16_t count = SILENCE_THRESHOLD;
while (count > 0) {
if (PIN_AX_READ != 0) {
while (WAIT_AX_FALLING);
@@ -117,7 +133,7 @@ void Bios_Patching(void) {
count--;
}
if (count == 0) {
current_confirms++; // Silence block confirmed
current_confirms++; // Validated one silence window
}
}

View File

@@ -225,8 +225,19 @@
#pragma once
#ifdef ATmega328_168
static inline void optimizePeripherals(void) {
/*------------------------------------------------------------------------------------------------
* FUNCTION : optimizePeripherals()
*
* DESCRIPTION :
* Minimizes power consumption and internal noise by disabling unused hardware modules.
* Performs a hard shutdown of peripherals to reduce current leakage and CPU jitter.
*
* 1. ANALOG SHUTDOWN: Disables ADC and Analog Comparator to prevent parasitic drain.
* 2. BUFFER DISABLE: Disconnects digital input buffers on analog pins (A0-A5).
* 3. CLOCK GATING (PRR): Shuts down clocks to TWI, SPI, and all Timers (0, 1, 2).
* 4. UART MAINTENANCE: Keeps PRUSART0 active to allow Serial communication.
------------------------------------------------------------------------------------------------*/
static inline void OptimizePeripherals(void) {
// 1. Disable Interrupts during setup
cli();
@@ -275,59 +286,57 @@
#define PIN_SQCK_INPUT DDRD &= ~(1 << DDD6) // Set DDRB register to configure PINB6 as input
#define PIN_SUBQ_INPUT DDRD &= ~(1 << DDD7) // Set DDRB register to configure PINB7 as input
// Enable pull-ups and set high on the main pins
// Configure lines as outputs (for injection/override)
#define PIN_DATA_OUTPUT DDRB |= (1 << DDB0) // Set DDRB register to configure PINB0 as output
#define PIN_WFCK_OUTPUT DDRB |= (1 << DDB1) // Set DDRB register to configure PINB1 as output
// Define pull-ups and set high at the main pin
#define PIN_DATA_SET PORTB |= (1 << PB0) // Set PORTB register to make PINB0 high (enable pull-up)
// Clear the main pins (set low)
// Bus line state control (Set High / Clear Low)
#define PIN_DATA_SET PORTB |= (1 << PB0) // Set PORTB register to make PINB0 high (enable pull-up))
#define PIN_DATA_CLEAR PORTB &= ~(1 << PB0) // Set PORTB register to make PINB0 low
#define PIN_WFCK_CLEAR PORTB &= ~(1 << PB1) // Set PORTB register to make PINB1 low
// Read the state of the main input pins
// Direct Register Reading (High-speed polling)
#define PIN_SQCK_READ (!!(PIND & (1 << PIND6))) // Check if the value of PIND6 is high (1)
#define PIN_SUBQ_READ (!!(PIND & (1 << PIND7))) // Check if the value of PIND7 is high (1)
#define PIN_WFCK_READ (!!(PINB & (1 << PINB1))) // Check if the value of PIND1 is high (1)
// LED pin handling (for indication)
// --- Status Indication (LED) ---
#ifdef LED_RUN
#define PIN_LED_OUTPUT DDRB |= (1 << DDB5) // Configure PINB5 as output (for LED)
#define PIN_LED_ON PORTB |= (1 << PB5) // Set PINB5 high (turn on LED)
#define PIN_LED_OFF PORTB &= ~(1 << PB5) // Set PINB5 low (turn off LED)
#endif
// Handling the BIOS patch
#if defined(SCPH_102) || defined(SCPH_100) || defined(SCPH_7500_9000) || defined(SCPH_7000) || defined(SCPH_5000_5500) || defined(SCPH_3500) || defined(SCPH_3000) || defined(SCPH_1000)
// Define input pins for the BIOS patch
#define PIN_AX_INPUT DDRD &= ~(1 << DDD2) // Set DDRD register to configure PIND2 as input
#define PIN_DX_INPUT DDRD &= ~(1 << DDD4) // Set DDRD register to configure PIND4 as input
// Define output pins for the BIOS patch
#define PIN_DX_OUTPUT DDRD |= (1 << DDD4) // Set DDRD register to configure PIND4 as output
// Set pull-ups high on output pins
#define PIN_DX_SET PORTD |= (1 << PD4) // Set PORTD register to make PIND4 high
// Set pull-ups low on output pins
#define PIN_DX_CLEAR PORTD &= ~(1 << PD4) // Set PORTD register to make PIND4 low
// --- BIOS Patching Configuration ---
#if defined(SCPH_102) || \
defined(SCPH_100) || \
defined(SCPH_7500_9000) || \
defined(SCPH_7000) || \
defined(SCPH_3500_5500) || \
defined(SCPH_3000) || \
defined(SCPH_1000)
// Address (AX) and Data (DX) lines for BIOS override
#define PIN_AX_INPUT DDRD &= ~(1 << DDD2)
#define PIN_DX_INPUT DDRD &= ~(1 << DDD4)
#define PIN_DX_OUTPUT DDRD |= (1 << DDD4)
#define PIN_DX_SET PORTD |= (1 << PD4)
#define PIN_DX_CLEAR PORTD &= ~(1 << PD4)
// Blocking wait macros for AX synchronization
#define WAIT_AX_RISING (!(PIND & (1 << PIND2))) // Wait for pulse start (Blocking until Rising Edge)
#define WAIT_AX_FALLING (PIND & (1 << PIND2)) // Wait for pulse end (Blocking until Falling Edge)
#define PIN_AX_READ (!!(PIND & (1 << PIND2)))
// Read the input pins for the BIOS patch
#define PIN_AX_READ (!!(PIND & (1 << PIND2))) // Read the state of PIND2
// Hardware Interrupt (INT0) for AX pulse counting
#define PIN_AX_INTERRUPT_ENABLE EIMSK |= (1<<INT0) // Enable external interrupt on INT0 (PINB2)
#define PIN_AX_INTERRUPT_DISABLE EIMSK &= ~(1<<INT0) // Disable external interrupt on INT0
#define PIN_AX_INTERRUPT_RISING EICRA |= (1<<ISC01)|(1<<ISC00) // Configure INT0 for rising edge trigger
#define PIN_AX_INTERRUPT_VECTOR INT0_vect // Interrupt vector for INT0 (external interrupt)
// Defin PIN_AY for HIGH_PATCH
#if defined(SCPH_3000) || defined(SCPH_1000)
// Secondary Address line (AY) for multi-stage patching (INT1)
#if defined(SCPH_3000) || \
defined(SCPH_1000)
#define PIN_AY_INPUT DDRD &= ~(1 << DDD3) // Set DDRD register to configure PIND3 as input
#define WAIT_AY_RISING (!(PIND & (1 << PIND3)))
#define WAIT_AY_FALLING (PIND & (1 << PIND3))
@@ -336,10 +345,9 @@
#define PIN_AY_INTERRUPT_DISABLE EIMSK &= ~(1<<INT1) // Disable external interrupt on INT1
#define PIN_AY_INTERRUPT_RISING EICRA |= (1<<ISC11)|(1<<ISC10) // Configure INT1 for rising edge trigger
#define PIN_AY_INTERRUPT_VECTOR INT1_vect // Interrupt vector for INT1 (external interrupt)
#endif
// Handle switch input for BIOS patch
// Hardware Bypass Switch (On-the-fly deactivation)
#if defined(SCPH_7000)
#define PIN_SWITCH_INPUT DDRD &= ~(1 << DDD5) // Configure PIND5 as input for switch
#define PIN_SWITCH_SET PORTD |= (1 << PD5) // Set PIND5 high (enable pull-up)
@@ -360,34 +368,42 @@
#ifdef ATmega32U4_16U4
static inline void optimizePeripherals(void) {
// --- Digital Input Configuration (A0-A5) ---
// On 32U4, A0-A5 are spread across Port F (F7-F4) and Port D (D4, D7)
// 1. Set all Analog/Digital pins to Input
DDRF &= ~((1<<DDF7)|(1<<DDF6)|(1<<DDF5)|(1<<DDF4)); // A0-A3
DDRD &= ~((1<<DDD4)|(1<<DDD7)); // A4-A5
// 2. Disable Digital Input Buffers (DIDR0/DIDR2)
// On 32U4, DIDR0 and DIDR2 control the digital buffer for ADC pins.
// Setting these bits to 0 ENABLES digital reads (PINF/PIND).
DIDR0 &= ~((1<<ADC7D)|(1<<ADC6D)|(1<<ADC5D)|(1<<ADC4D)); // A0-A3
DIDR2 &= ~((1<<ADC10D)|(1<<ADC8D)); // A4-A5
static inline void OptimizePeripherals(void) {
// 1. Global Interrupt Disable during hardware reconfiguration
cli();
// --- Analog Modules Shutdown (32U4 specific) ---
// 1. Disable the ADC module
ADCSRA &= ~(1 << ADEN);
// 2. Disable the Analog Comparator
ACSR |= (1 << ACD);
// 2. Analog Front-End Shutdown
ADCSRA &= ~(1 << ADEN); // Disable ADC
ACSR |= (1 << ACD); // Disable Analog Comparator
// --- Power Reduction Register (32U4 Hard Clock Shut Off) ---
// PRR0 and PRR1 handle the clocks on 32U4.
// Note: We DO NOT touch PRUSB if we want to keep the Serial/USB alive.
PRR0 |= (1 << PRADC) | (1 << PRTIM0) | (1 << PRTIM1);
PRR1 |= (1 << PRTIM3); // 32U4 has an extra Timer 3
// 3. Digital Input Buffer Disable (DIDR0 & DIDR2)
// 32U4 has more analog channels (ADC0-ADC7 and ADC8-ADC13)
DIDR0 = 0xFF; // Disable digital buffers on F0-F7
DIDR2 = 0x3F; // Disable digital buffers on D4, D6, D7, B4, B5, B6
// --- Timer 0 Specific Shutdown ---
TCCR0B = 0;
TIMSK0 = 0;
// 4. GPIO Strategy (Unused pins to Pull-up)
// On 32U4, Port C is small (only PC6/PC7). Adjusting to cover most unused pins.
PORTC |= 0xFF;
PORTE |= 0xFF; // Extra port on 32U4
// 5. Power Reduction Registers (PRR0 & PRR1)
// PRR0 handles TWI, SPI, Timers 0, 1 and ADC.
PRR0 = (1 << PRTWI) | // I2C Off
(1 << PRSPI) | // SPI Off
(1 << PRTIM0) | // Timer 0 Off
(1 << PRTIM1) | // Timer 1 Off
(1 << PRADC); // ADC Clock Off
// PRR1 handles Timer 3, Timer 4 and USB.
// We KEEP PRUSART1 (Serial1) and PRUSB active for communication.
PRR1 = (1 << PRTIM3) | // Timer 3 Off
(1 << PRTIM4); // Timer 4 Off (High speed timer)
// 6. Double Security for Timer 0 (Redundancy)
TCCR0B = 0;
TIMSK0 = 0;
// 7. Note: USB/Serial1 remain functional unless PRUSB/PRUSART1 are set.
}
//#define F_CPU 16000000L
@@ -400,111 +416,184 @@
#include <avr/sfr_defs.h>
#include <util/delay.h>
// Globale interrupt seting
#define GLOBAL_INTERRUPT_ENABLE SREG |= (1 << 7)
#define GLOBAL_INTERRUPT_DISABLE SREG &= ~(1 << 7)
// --- Main Bus Interface (CD-ROM Controller) ---
// Define the main pins as inputs
#define PIN_DATA_INPUT DDRB &= ~(1 << DDB4) // DATA line (PB4)
#define PIN_WFCK_INPUT DDRB &= ~(1 << DDB5) // WFCK / GATE line (PB5)
#define PIN_SQCK_INPUT DDRD &= ~(1 << DDD7) // SQCK Clock (PD7)
#define PIN_SUBQ_INPUT DDRE &= ~(1 << DDE6) // SUBQ Data (PE6)
// Handling the main pins
// Main pins
#define PIN_DATA_INPUT DDRB &= ~(1 << DDB4)
#define PIN_WFCK_INPUT DDRB &= ~(1 << DDB5)
#define PIN_SQCK_INPUT DDRD &= ~(1 << DDD7)
#define PIN_SUBQ_INPUT DDRE &= ~(1 << DDE6)
// Configure lines as outputs (for injection/override)
#define PIN_DATA_OUTPUT DDRB |= (1 << DDB4)
#define PIN_WFCK_OUTPUT DDRB |= (1 << DDB5)
// Define pull-ups and set high at the main pin
#define PIN_DATA_SET PORTB |= (1 << PB4)
// Define pull-ups set down at the main pin
#define PIN_DATA_CLEAR PORTB &= ~(1 << PB4)
#define PIN_WFCK_CLEAR PORTB &= ~(1 << PB5)
// Read the main pins
#define PIN_SQCK_READ (PIND & (1 << PIND7))
#define PIN_SUBQ_READ (PINE & (1 << PINE6))
#define PIN_WFCK_READ (PINB & (1 << PINB5))
// Bus line state control (Set High / Clear Low)
#define PIN_DATA_SET PORTB |= (1 << PB4) // Enable pull-up or drive HIGH
#define PIN_DATA_CLEAR PORTB &= ~(1 << PB4) // Drive line LOW
#define PIN_WFCK_CLEAR PORTB &= ~(1 << PB5) // Drive line LOW
// Handling and use of the LED pin
// Direct Register Reading (High-speed polling)
#define PIN_SQCK_READ (PIND & (1 << PIND7))
#define PIN_SUBQ_READ (PINE & (1 << PINE6))
#define PIN_WFCK_READ (PINB & (1 << PINB5))
// --- Status Indication (LED) ---
#ifdef LED_RUN
#define PIN_LED_OUTPUT DDRB |= (1 << DDB6)
#define PIN_LED_ON PORTB |= (1 << PB6)
#define PIN_LED_OFF PORTB &= ~(1 << PB6)
#define PIN_LED_OUTPUT DDRB |= (1 << DDB6) // LED on PB6
#define PIN_LED_ON PORTB |= (1 << PB6)
#define PIN_LED_OFF PORTB &= ~(1 << PB6)
#endif
// Handling the BIOS patch
#if defined(SCPH_102) || defined(SCPH_102_legacy) || defined(SCPH_100) || defined(SCPH_7000_9000) || defined(SCPH_5500) || defined(SCPH_3500_5000) || defined(SCPH_3000) || defined(SCPH_1000)
// Pins input
#define PIN_AX_INPUT DDRD &= ~(1 << DDD1)
#define PIN_AY_INPUT DDRD &= ~(1 << DDD0)
#define PIN_DX_INPUT DDRD &= ~(1 << DDD4)
// Pin output
#define PIN_DX_OUTPUT DDRD |= (1 << DDD4)
// Define pull-ups set high
#define PIN_DX_SET PORTD |= (1 << PD4)
// Define pull-ups set down
#define PIN_DX_CLEAR PORTD &= ~(1 << PD4)
// Read pins for BIOS patch
#define PIN_AX_READ (PIND & (1 << PIND1))
#define PIN_AY_READ (PIND & (1 << PIND0))
// Handling and reading the switch pin for patch BIOS
#ifdef PATCH_SWITCH
#define PIN_SWITCH_INPUT DDRC &= ~(1 << DDC6)
#define PIN_SWITCH_SET PORTC |= (1 << PC6)
#define PIN_SWITCH_READ (PINC & (1 << PINC6))
// --- BIOS Patching Configuration (32U4 Mapping) ---
#if defined(SCPH_102) || defined(SCPH_102_legacy) || defined(SCPH_100) || \
defined(SCPH_7000_9000) || defined(SCPH_5500) || defined(SCPH_3500_5000) || \
defined(SCPH_3000) || defined(SCPH_1000)
// Address (AX / AY) and Data (DX) lines for BIOS override
#define PIN_AX_INPUT DDRD &= ~(1 << DDD1) // AX on PD1 (INT1)
#define PIN_AY_INPUT DDRD &= ~(1 << DDD0) // AY on PD0 (INT0)
#define PIN_DX_INPUT DDRD &= ~(1 << DDD4) // DX on PD4
#define PIN_DX_OUTPUT DDRD |= (1 << DDD4)
#define PIN_DX_SET PORTD |= (1 << PD4)
#define PIN_DX_CLEAR PORTD &= ~(1 << PD4)
// Blocking wait macros for synchronization
#define WAIT_AX_RISING (!(PIND & (1 << PIND1))) // Wait for pulse start
#define WAIT_AX_FALLING (PIND & (1 << PIND1)) // Wait for pulse end
#define PIN_AX_READ (PIND & (1 << PIND1))
// Hardware Interrupt (INT1) for AX pulse counting
#define PIN_AX_INTERRUPT_ENABLE EIMSK |= (1 << INT1)
#define PIN_AX_INTERRUPT_DISABLE EIMSK &= ~(1 << INT1)
#define PIN_AX_INTERRUPT_RISING EICRA |= (1 << ISC11) | (1 << ISC10)
#define PIN_AX_INTERRUPT_VECTOR INT1_vect
// Secondary Address line (AY) for multi-stage patching (INT0)
#if defined(SCPH_3000) || defined(SCPH_1000)
#define PIN_AY_READ (PIND & (1 << PIND0))
#define WAIT_AY_RISING (!(PIND & (1 << PIND0)))
#define WAIT_AY_FALLING (PIND & (1 << PIND0))
#define PIN_AY_INTERRUPT_ENABLE EIMSK |= (1 << INT0)
#define PIN_AY_INTERRUPT_DISABLE EIMSK &= ~(1 << INT0)
#define PIN_AY_INTERRUPT_RISING EICRA |= (1 << ISC01) | (1 << ISC00)
#define PIN_AY_INTERRUPT_VECTOR INT0_vect
#endif
// BIOS timer clear
#define TIMER_TIFR_CLEAR TIFR0 |= (1 << OCF0A)
// Handling the external interrupt
#define PIN_AX_INTERRUPT_ENABLE EIMSK |= (1 << INT1)
#define PIN_AY_INTERRUPT_ENABLE EIMSK |= (1 << INT0)
#define PIN_AX_INTERRUPT_DISABLE EIMSK &= ~(1 << INT1)
#define PIN_AY_INTERRUPT_DISABLE EIMSK &= ~(1 << INT0)
#define PIN_AX_INTERRUPT_RISING EICRA |= (1 << ISC11) | (1 << ISC10)
#define PIN_AY_INTERRUPT_RISING EICRA |= (1 << ISC01) | (1 << ISC00)
#define PIN_AX_INTERRUPT_FALLING (EICRA = (EICRA & ~(1 << ISC10)) | (1 << ISC11))
#define PIN_AY_INTERRUPT_FALLING (EICRA = (EICRA & ~(1 << ISC00)) | (1 << ISC01))
#define PIN_AX_INTERRUPT_VECTOR INT1_vect
#define PIN_AY_INTERRUPT_VECTOR INT0_vect
// Hardware Bypass Switch (On-the-fly deactivation)
#ifdef (SCPH_7000)
#define PIN_SWITCH_INPUT DDRC &= ~(1 << DDC6) // Bypass on PC6
#define PIN_SWITCH_SET PORTC |= (1 << PC6) // Enable pull-up
#define PIN_SWITCH_READ (PINC & (1 << PINC6))
#endif
#endif
#endif
// // Globale interrupt seting
// #define GLOBAL_INTERRUPT_ENABLE SREG |= (1 << 7)
// #define GLOBAL_INTERRUPT_DISABLE SREG &= ~(1 << 7)
// // Handling the main pins
// // Main pins
// #define PIN_DATA_INPUT DDRB &= ~(1 << DDB4)
// #define PIN_WFCK_INPUT DDRB &= ~(1 << DDB5)
// #define PIN_SQCK_INPUT DDRD &= ~(1 << DDD7)
// #define PIN_SUBQ_INPUT DDRE &= ~(1 << DDE6)
// #define PIN_DATA_OUTPUT DDRB |= (1 << DDB4)
// #define PIN_WFCK_OUTPUT DDRB |= (1 << DDB5)
// // Define pull-ups and set high at the main pin
// #define PIN_DATA_SET PORTB |= (1 << PB4)
// // Define pull-ups set down at the main pin
// #define PIN_DATA_CLEAR PORTB &= ~(1 << PB4)
// #define PIN_WFCK_CLEAR PORTB &= ~(1 << PB5)
// // Read the main pins
// #define PIN_SQCK_READ (PIND & (1 << PIND7))
// #define PIN_SUBQ_READ (PINE & (1 << PINE6))
// #define PIN_WFCK_READ (PINB & (1 << PINB5))
// // Handling and use of the LED pin
// #ifdef LED_RUN
// #define PIN_LED_OUTPUT DDRB |= (1 << DDB6)
// #define PIN_LED_ON PORTB |= (1 << PB6)
// #define PIN_LED_OFF PORTB &= ~(1 << PB6)
// #endif
// // Handling the BIOS patch
// #if defined(SCPH_102) || defined(SCPH_102_legacy) || defined(SCPH_100) || defined(SCPH_7000_9000) || defined(SCPH_5500) || defined(SCPH_3500_5000) || defined(SCPH_3000) || defined(SCPH_1000)
// // Pins input
// #define PIN_AX_INPUT DDRD &= ~(1 << DDD1)
// #define PIN_AY_INPUT DDRD &= ~(1 << DDD0)
// #define PIN_DX_INPUT DDRD &= ~(1 << DDD4)
// // Pin output
// #define PIN_DX_OUTPUT DDRD |= (1 << DDD4)
// // Define pull-ups set high
// #define PIN_DX_SET PORTD |= (1 << PD4)
// // Define pull-ups set down
// #define PIN_DX_CLEAR PORTD &= ~(1 << PD4)
// // Read pins for BIOS patch
// #define PIN_AX_READ (PIND & (1 << PIND1))
// #define PIN_AY_READ (PIND & (1 << PIND0))
// // Handling and reading the switch pin for patch BIOS
// #ifdef PATCH_SWITCH
// #define PIN_SWITCH_INPUT DDRC &= ~(1 << DDC6)
// #define PIN_SWITCH_SET PORTC |= (1 << PC6)
// #define PIN_SWITCH_READ (PINC & (1 << PINC6))
// #endif
// // BIOS timer clear
// #define TIMER_TIFR_CLEAR TIFR0 |= (1 << OCF0A)
// // Handling the external interrupt
// #define PIN_AX_INTERRUPT_ENABLE EIMSK |= (1 << INT1)
// #define PIN_AY_INTERRUPT_ENABLE EIMSK |= (1 << INT0)
// #define PIN_AX_INTERRUPT_DISABLE EIMSK &= ~(1 << INT1)
// #define PIN_AY_INTERRUPT_DISABLE EIMSK &= ~(1 << INT0)
// #define PIN_AX_INTERRUPT_RISING EICRA |= (1 << ISC11) | (1 << ISC10)
// #define PIN_AY_INTERRUPT_RISING EICRA |= (1 << ISC01) | (1 << ISC00)
// #define PIN_AX_INTERRUPT_FALLING (EICRA = (EICRA & ~(1 << ISC10)) | (1 << ISC11))
// #define PIN_AY_INTERRUPT_FALLING (EICRA = (EICRA & ~(1 << ISC00)) | (1 << ISC01))
// #define PIN_AX_INTERRUPT_VECTOR INT1_vect
// #define PIN_AY_INTERRUPT_VECTOR INT0_vect
// #endif
//#endif
#ifdef ATtiny85_45_25
static inline void optimizePeripherals(void) {
// --- Analog Modules Shutdown (ATtiny Specific) ---
// 1. Disable the ADC module
ADCSRA &= ~(1 << ADEN);
// 2. Disable the Analog Comparator
ACSR |= (1 << ACD);
static inline void OptimizePeripherals(void) {
// 1. Global Interrupt Disable during reconfiguration
cli();
// --- Power Reduction Register (Hard Clock Shut Off) ---
// On ATtiny85, PRR controls Timer 0, Timer 1, USI, and ADC.
// We stop the ADC and Timer 0 clocks.
PRR |= (1 << PRADC) | (1 << PRTIM0);
// 2. Analog Modules Shutdown
ADCSRA &= ~(1 << ADEN); // Disable ADC
ACSR |= (1 << ACD); // Disable Analog Comparator
// --- Timer 0 Specific Shutdown ---
TCCR0B = 0;
TIMSK = 0; // On ATtiny85, it's TIMSK (not TIMSK0)
}
// 3. Digital Input Buffer Disable (DIDR0)
// Disconnects digital buffers on PB0-PB5 to prevent leakage
DIDR0 = 0x3F;
#define DF_CPU 8000000L
#define TIMER_TCNT_CLEAR TCNT0 = 0x00;
#define SET_OCROA_DIV OCR0A = 79; //OCR0A Output Compare Register A,100KHz
#define SET_TIMER_TCCROA TCCR0A |= (1 << WGM01);
#define SET_TIMER_TCCROB TCCR0B |= (1 << CS00);
#define CTC_TIMER_VECTOR TIMER0_COMPA_vect
// 4. Power Reduction Register (PRR)
// Shuts down clocks to ADC and Timer 0.
// We KEEP USI or Timer 1 if required for specific logic.
PRR |= (1 << PRADC) | (1 << PRTIM0);
// 5. Timer 0 Specific Shutdown (Hardware Redundancy)
TCCR0B = 0;
TIMSK &= ~(1 << OCIE0A); // Disable Timer 0 interrupts
}
#include <stdint.h>
@@ -514,59 +603,109 @@ static inline void optimizePeripherals(void) {
#include <avr/sfr_defs.h>
#include <util/delay.h>
// Globale interrupt seting
#define GLOBAL_INTERRUPT_ENABLE SREG |= (1 << 7)
#define GLOBAL_INTERRUPT_DISABLE SREG &= ~(1 << 7)
// Handling the main pins
// --- Main Bus Interface (CD-ROM Controller) ---
// Main pins input
#define PIN_DATA_INPUT DDRB &= ~(1 << DDB2)
#define PIN_WFCK_INPUT DDRB &= ~(1 << DDB4)
#define PIN_SQCK_INPUT DDRB &= ~(1 << DDB0)
#define PIN_SUBQ_INPUT DDRB &= ~(1 << DDB1)
// Define the main pins as inputs
#define PIN_DATA_INPUT DDRB &= ~(1 << DDB2) // DATA line (PB2)
#define PIN_WFCK_INPUT DDRB &= ~(1 << DDB4) // WFCK / GATE line (PB4)
#define PIN_SQCK_INPUT DDRB &= ~(1 << DDB0) // SQCK Clock (PB0)
#define PIN_SUBQ_INPUT DDRB &= ~(1 << DDB1) // SUBQ Data (PB1)
// Main pin output
// Configure lines as outputs (for injection/override)
#define PIN_DATA_OUTPUT DDRB |= (1 << DDB2)
#define PIN_WFCK_OUTPUT DDRB |= (1 << DDB4)
// Define pull-ups and set high at the main pin
#define PIN_DATA_SET PORTB |= (1 << PB2)
// Bus line state control (Set High / Clear Low)
#define PIN_DATA_SET PORTB |= (1 << PB2) // Enable pull-up or drive HIGH
#define PIN_DATA_CLEAR PORTB &= ~(1 << PB2) // Drive line LOW
#define PIN_WFCK_CLEAR PORTB &= ~(1 << PB4) // Drive line LOW
// Define pull-ups set down at the main pin
#define PIN_DATA_CLEAR PORTB &= ~(1 << PB2)
#define PIN_WFCK_CLEAR PORTB &= ~(1 << PB4)
// Direct Register Reading (High-speed polling)
#define PIN_SQCK_READ (PINB & (1 << PINB0))
#define PIN_SUBQ_READ (PINB & (1 << PINB1))
#define PIN_WFCK_READ (PINB & (1 << PINB4))
// Read the main pins
#define PIN_SQCK_READ (PINB & (1 << PINB0))
#define PIN_SUBQ_READ (PINB & (1 << PINB1))
#define PIN_WFCK_READ (PINB & (1 << PINB4))
#define TIMER_INTERRUPT_ENABLE TIMSK |= (1 << OCIE0A)
// Timer Interrupt Management
#define TIMER_INTERRUPT_ENABLE TIMSK |= (1 << OCIE0A)
#define TIMER_INTERRUPT_DISABLE TIMSK &= ~(1 << OCIE0A)
// Handling and use of the LED pin
// --- Status Indication (LED) ---
#ifdef LED_RUN
#define PIN_LED_OUTPUT DDRB |= (1 << DDB3)
#define PIN_LED_ON PORTB |= (1 << PB3)
#define PIN_LED_OFF PORTB &= ~(1 << PB3)
#endif
#if defined(SCPH_1000) || defined(SCPH_3000) || defined(SCPH_3500_5000) || defined(SCPH_5500) || defined(SCPH_7000_9000) || defined(SCPH_100) || defined(SCPH_102_legacy) || defined(SCPH_102)
#error "ATtiny85_45_25 Not compatible with BIOS patch"
#define PIN_LED_OUTPUT DDRB |= (1 << DDB3) // LED on PB3
#define PIN_LED_ON PORTB |= (1 << PB3)
#define PIN_LED_OFF PORTB &= ~(1 << PB3)
#endif
// --- Debug Serial (Software Serial) ---
#if defined(PSNEE_DEBUG_SERIAL_MONITOR)
#include <SoftwareSerial.h>
SoftwareSerial mySerial(-1, 3); // RX, TX. (RX -1 = off)
// SoftwareSerial(RX, TX): RX set to -1 (disabled), TX on PB3
// Note: Resource conflict if LED_RUN is also on PB3
SoftwareSerial mySerial(-1, 3);
#define PIN_TXD_OUTPUT DDRB |= (1 << DDB3)
// #define DEBUG_PRINT(x) mySerial.print(x)
// #define DEBUG_PRINTHEX(x) mySerial.print(x, HEX)
// #define DEBUG_PRINTLN(x) mySerial.println(x)
// #define DEBUG_FLUSH mySerial.flush()
#endif
// --- Safety Check: BIOS Patch Compatibility ---
#if defined(SCPH_1000) || \
defined(SCPH_3000) || \
defined(SCPH_3500_5000) || \
defined(SCPH_5500) || \
defined(SCPH_7000_9000) || \
defined(SCPH_100) || \
defined(SCPH_102)
#error "ATtiny85/45/25 architecture is not compatible with the BIOS patch feature."
#endif
#endif
// // Handling the main pins
// // Main pins input
// #define PIN_DATA_INPUT DDRB &= ~(1 << DDB2)
// #define PIN_WFCK_INPUT DDRB &= ~(1 << DDB4)
// #define PIN_SQCK_INPUT DDRB &= ~(1 << DDB0)
// #define PIN_SUBQ_INPUT DDRB &= ~(1 << DDB1)
// // Main pin output
// #define PIN_DATA_OUTPUT DDRB |= (1 << DDB2)
// #define PIN_WFCK_OUTPUT DDRB |= (1 << DDB4)
// // Define pull-ups and set high at the main pin
// #define PIN_DATA_SET PORTB |= (1 << PB2)
// // Define pull-ups set down at the main pin
// #define PIN_DATA_CLEAR PORTB &= ~(1 << PB2)
// #define PIN_WFCK_CLEAR PORTB &= ~(1 << PB4)
// // Read the main pins
// #define PIN_SQCK_READ (PINB & (1 << PINB0))
// #define PIN_SUBQ_READ (PINB & (1 << PINB1))
// #define PIN_WFCK_READ (PINB & (1 << PINB4))
// #define TIMER_INTERRUPT_ENABLE TIMSK |= (1 << OCIE0A)
// #define TIMER_INTERRUPT_DISABLE TIMSK &= ~(1 << OCIE0A)
// // Handling and use of the LED pin
// #ifdef LED_RUN
// #define PIN_LED_OUTPUT DDRB |= (1 << DDB3)
// #define PIN_LED_ON PORTB |= (1 << PB3)
// #define PIN_LED_OFF PORTB &= ~(1 << PB3)
// #endif
// #if defined(SCPH_1000) || defined(SCPH_3000) || defined(SCPH_3500_5000) || defined(SCPH_5500) || defined(SCPH_7000_9000) || defined(SCPH_100) || defined(SCPH_102_legacy) || defined(SCPH_102)
// #error "ATtiny85_45_25 Not compatible with BIOS patch"
// #endif
// #if defined(PSNEE_DEBUG_SERIAL_MONITOR)
// #include <SoftwareSerial.h>
// SoftwareSerial mySerial(-1, 3); // RX, TX. (RX -1 = off)
// #define PIN_TXD_OUTPUT DDRB |= (1 << DDB3)
// // #define DEBUG_PRINT(x) mySerial.print(x)
// // #define DEBUG_PRINTHEX(x) mySerial.print(x, HEX)
// // #define DEBUG_PRINTLN(x) mySerial.println(x)
// // #define DEBUG_FLUSH mySerial.flush()
// #endif
// #endif
// *****************************************************************************************************************
// WARNING:

View File

@@ -47,8 +47,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
//#define SCPH_100 // DX - D0 | AX - A7 | | 4.3j - CRC F2AF798B
//#define SCPH_7500_9000 // DX - D0 | AX - A7 | | 4.0j - CRC EC541CD0
//#define SCPH_7000 // DX - D0 | AX - A7 | | 4.0j - CRC EC541CD0 Enables hardware support for disabling BIOS patching.
//#define SCPH_5000_5500 // DX - D0 | AX - A15 | | 3.0j - CRC FF3EEB8C, 2.2j - CRC 24FC7E17
//#define SCPH_3500 // DX - D0 | AX - A15 | AX - A15 | 2.1j - CRC BC190209
//#define SCPH_3500_5500 // DX - D0 | AX - A16 | AX - A15 | 3.0j - CRC FF3EEB8C, 2.2j - CRC 24FC7E17, 2.1j - CRC BC190209
//#define SCPH_3000 // DX - D5 | AX - A7, AY - A8 | AX - A6, AY - A7 | 1.1j - CRC 3539DEF6
//#define SCPH_1000 // DX - D5 | AX - A7, AY - A8 | AX - A6, AY - A7 | 1.0j - CRC 3B601FC8
@@ -130,7 +129,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
volatile uint8_t wfck_mode = 0;
// Variables de contrôle globales
// Global buffer to store the 12-byte Sub-Q channel data
// Global buffer to store the 12-byte SUBQ channel data
uint8_t subqBuffer[12];
uint8_t hysteresis = 0;
@@ -145,58 +144,67 @@ uint8_t hysteresis = 0;
/*----------------------------------------------------------------------
Function: board_detection
* FUNCTION : BoardDetection
*
* DESCRIPTION :
* Distinguishes motherboard generations (PU-7 through PU-22+) by analyzing
* the behavior of the WFCK signal.
* SIGNAL CHARACTERISTICS:
* - Legacy Boards (PU-7 to PU-20): WFCK acts as a static GATE signal.
* It remains HIGH (continuous) during the region-check window.
* - Modern Boards (PU-22 or newer): WFCK is an oscillating clock signal
* (Frequency-based).
*
* WFCK: __----------------------- // CONTINUOUS (PU-7 .. PU-20)(GATE)
*
* WFCK: __-_-_-_-_-_-_-_-_-_-_-_- // FREQUENCY (PU-22 or newer)
*
*
* HISTORICAL CONTEXT:
* Traditionally, WFCK was referred to as the "GATE" signal. On early models,
* modchips functioned as a synchronized gate, pulling the signal LOW
* precisely when the region-lock data was being processed.
*
* FREQUENCY DATA:
* - Initial/Protection Phase: ~7.3 kHz.
* - Standard Data Reading: ~14.6 kHz.
*
*-----------------------------------------------------------------------*/
This function distinguishes motherboard generations by detecting
the nature of the WFCK signal:
WFCK: __----------------------- // CONTINUOUS (PU-7 .. PU-20)(GATE)
WFCK: __-_-_-_-_-_-_-_-_-_-_-_- // FREQUENCY (PU-22 or newer)
Traditionally, the WFCK signal was called GATE. This is because, on early models,
modchips acted like a gate that would open to pull the signal down
at the exact moment the region code was being passed (which is still the case today).
During the initialization and region protection zone reading phases,
the WFCK clock frequency is approximately 7.3 kHz.
During normal data reading, the frequency shifts to 14.6 kHz.
-----------------------------------------------------------------------*/
void board_detection() {
// Default to static signal (PU-7 to PU-20)
void BoardBetection() {
// Default state: 0 (Static/GATE mode for PU-7 to PU-20)
wfck_mode = 0;
/*
INITIAL STABILIZATION DELAY (300ms)
PU-7 to PU-20: Voltage climbs slowly (up to 54ms) then stays HIGH (static).
PU-22+: Signal only starts oscillating (~7.3kHz) after approximately 297ms.
Waiting 300ms bypasses all power-up transients and initial noise.
*/
/**
* INITIAL STABILIZATION DELAY (300ms)
* - PU-7/20: Voltage ramp-up takes ~54ms before stabilizing at a logic HIGH.
* - PU-22+: Clock oscillation (~7.3kHz) typically begins after ~297ms.
* This delay ensures all power-up transients and initial noise are bypassed.
*/
_delay_ms(300);
// Sampling window to detect the oscillating signal
// Define a sampling window to capture potential oscillations
uint16_t detectionWindow = 10000;
while (--detectionWindow) {
/*
On older boards (PU-7/20), the signal is now a solid HIGH.
If we detect a LOW state, it's a potential oscillation from a newer board.
*/
/**
* DETECTION LOGIC:
* On legacy boards (PU-7/20), WFCK is now a steady HIGH.
* Detecting a LOW state indicates a potential clock cycle from a newer board.
*/
if (!PIN_WFCK_READ) {
// Small debounce delay to filter out micro-glitches or remaining noise
// Software debounce to filter out micro-glitches or parasitic noise
uint8_t debounce = 100;
while (--debounce);
/*
VERIFICATION: If the signal is STILL low, it confirms a real
clock cycle (WFCK). Older boards will never reach this state
once stabilized at HIGH.
*/
/**
* VERIFICATION:
* If the signal remains LOW after the debounce, we confirm a genuine
* clock oscillation (WFCK). Legacy boards never transition to LOW
* once stabilized at HIGH during this phase.
*/
if (!PIN_WFCK_READ) {
wfck_mode = 1; // Target: PU-22 or newer
wfck_mode = 1; // Target confirmed: PU-22 or newer (Frequency mode)
return;
}
}
@@ -207,14 +215,18 @@ void board_detection() {
#endif
}
//******************************************************************************************************************
// Reads a complete 12-byte SUBQ transmission from the CD drive.
// Uses clock-edge synchronization and includes a safety timeout for malformatted streams.
//******************************************************************************************************************
/******************************************************************************************************************
* FUNCTION : CaptureSUBQ
*
* DESCRIPTION :
* Captures a complete 12-byte SUBQ frame from the CD controller.
* Synchronizes with the SQCK (Sub-Q Clock) pin to shift in serial data from
* the SUBQ pin. This implementation follows the standard PlayStation CDIC
* protocol (Synchronous Serial, LSB first).
******************************************************************************************************************/
void captureSubQ(void) {
// Total bytes to read from the CD-ROM subcode channel
uint8_t bytesRemaining = 12;
void CaptureSUBQ(void) {
uint8_t bytesRemaining = 12; // Total frame size for a complete SUBQ
uint8_t* bufferPtr = subqBuffer;
do {
@@ -222,12 +234,19 @@ void captureSubQ(void) {
uint8_t bitsToRead = 8;
while (bitsToRead--) {
// PHASE 1: Wait for Clock (SQCK) to go LOW then HIGH (Sampling on Rising Edge)
// This ensures data is stable on the SUBQ pin before reading.
/**
* PHASE 1: BIT SYNCHRONIZATION (SQCK)
* The CD controller signals a new bit by toggling the Clock line.
* Data is sampled on the RISING EDGE of SQCK for maximum stability.
*/
while (PIN_SQCK_READ); // Wait for falling edge
while (!PIN_SQCK_READ); // Wait for rising edge
// PHASE 2: Shift bit into the byte (LSB first)
/**
* PHASE 2: BIT ACQUISITION & SHIFTING
* The PlayStation SUBQ bus transmits data LSB (Least Significant Bit) first.
* We shift the current byte right and inject the new bit into the MSB (0x80).
*/
currentByte >>= 1;
if (PIN_SUBQ_READ) {
currentByte |= 0x80;
@@ -235,39 +254,46 @@ void captureSubQ(void) {
}
// Store reconstructed byte and advance pointer
*bufferPtr++ = currentByte;
} while (--bytesRemaining); // Faster than (bytesRemaining < 12)
} while (--bytesRemaining); // Efficient countdown for AVR binary size
#if defined(PSNEE_DEBUG_SERIAL_MONITOR)
logSubQ(subqBuffer);
LogSUBQ(subqBuffer);
#endif
}
/**************************************************************************************
* Processes sector data for the SCPH-5903 (Dual-interface PS1) to differentiate
* between PlayStation games and Video CDs (VCD).
/******************************************************************************************
* FUNCTION : Filter_SUBQ_Samples() [SCPH_5903 Dual-Interface Variant]
*
* This heuristic uses an 'hysteresis' counter to stabilize disc detection:
* - Increases when a PSX Lead-In or valid game sector is identified.
* - Remains neutral/ignores VCD-specific Lead-In patterns.
* - Decreases (fades out) when the data does not match known patterns.
*
* isDataSector Boolean flag indicating if the current sector contains data.
**************************************************************************************/
* DESCRIPTION :
* Parses and filters the raw serial data stream from the SUBQ pin specifically
* for the SCPH-5903 model to differentiate between Game Discs and Video CDs (VCD).
*
* 1. VCD EXCLUSION: Specifically filters out VCD Lead-In patterns (sub-mode 0x02)
* to prevent incorrect region injection on non-game media.
* 2. SUBQ HIT COUNTING: Increments 'hysteresis' for valid PlayStation TOC (A0-A2)
* markers or active game tracking.
* 3. SIGNAL DECAY: Decrements the counter when SUBQ samples match VCD patterns
* or unknown data, ensuring stable disc identification.
*
* INPUT : isDataSector (bool) - Filtered flag based on raw sector control bits.
******************************************************************************************/
#ifdef SCPH_5903
void processLogic(uint8_t isDataSector) {
void FilterSUBQSamples(uint8_t isDataSector) {
uint8_t currentHysteresis = hysteresis;
// Fast filtering: most sectors fail here by checking sync markers (index 1 and 6)
// --- STEP 1: SUBQ Frame Synchronization ---
// Fast filtering: ignore raw data if sync markers (index 1 and 6) are not 0x00.
if (subqBuffer[1] == 0x00 && subqBuffer[6] == 0x00) {
uint8_t pointAddress = subqBuffer[2];
/*
INCREMENT CONDITIONS:
1. Valid PSX Lead-In: data sector AND Point A0-A2 range AND NOT VCD (sub-mode 0x02).
2. Tracking Maintenance: Valid sector (Mode 0x01 or Data) while already synchronized.
*/
/**
* HIT INCREMENT CONDITIONS:
* A. VALID PSX LEAD-IN: Data sector AND Point A0-A2 range AND NOT VCD (sub-mode != 0x02).
* (uint8_t)(pointAddress - 0xA0) <= 2 is an optimized check for 0xA0, 0xA1, 0xA2.
* B. TRACKING MAINTENANCE: Keeps count if already synced and reading Mode 0x01 or Data.
*/
if ( (isDataSector && (uint8_t)(pointAddress - 0xA0) <= 2 && subqBuffer[3] != 0x02) ||
(currentHysteresis > 0 && (subqBuffer[0] == 0x01 || isDataSector)) )
{
@@ -276,46 +302,45 @@ void captureSubQ(void) {
}
}
/*
DECREMENT CONDITION:
No match found or VCD Lead-In detected. Slowly decrease confidence level.
*/
// --- STEP 2: Signal Decay / Pattern Mismatch ---
// Decrement the hit counter if no valid PSX pattern is detected in the SUBQ stream.
if (currentHysteresis > 0) {
hysteresis = currentHysteresis - 1;
}
}
#else
/******************************************************************************************
* Heuristic logic for standard PlayStation hardware (Non-VCD models).
* FUNCTION : Filter_SUBQ_Samples()
*
* This function monitors disc sectors to identify genuine PlayStation discs:
* 1. Checks for specific Lead-In markers (Point A0, A1, A2 or Track 01).
* 2. Uses an incrementing 'hysteresis' counter to confirm disc validity.
* 3. Includes a 'fade-out' mechanism to reduce the counter if valid patterns are lost,
* effectively filtering out noise or read errors.
*
* isDataSector Boolean flag: true if the current sector is a data sector.
******************************************************************************************/
* DESCRIPTION :
* Parses and filters the raw serial data stream from the SUBQ pin.
* Increments a hit counter (hysteresis) when specific patterns are identified
* in the SUBQ stream, confirming the laser is reading the region-check area.
*
* 1. RAW BUS FILTERING: Validates SUBQ framing by checking sync markers (index 1 & 6).
* 2. PATTERN MATCHING: Detects Lead-In TOC (A0-A2) or Track 01 at the spiral start.
* 3. SIGNAL DECAY: Decrements the counter if the current SUBQ sample does not
* match expected PlayStation protection patterns.
*
* INPUT : isDataSector (bool) - Filtered flag based on raw sector control bits.
******************************************************************************************/
void processLogic(uint8_t isDataSector) {
void FilterSUBQSamples(uint8_t isDataSector) {
uint8_t currentHysteresis = hysteresis;
// Fast filtering: most sectors fail here by checking sync markers (index 1 and 6)
// --- STEP 1: SUBQ Frame Synchronization ---
// Ignore the raw bitstream unless sync markers (1 & 6) are 0x00.
if (subqBuffer[1] == 0x00 && subqBuffer[6] == 0x00) {
uint8_t pointAddress = subqBuffer[2];
/*
INCREMENT CONDITIONS:
1. Lead-In Detection:
- Point A0-A2 or higher (TOC info).
- OR Point 01 with a timestamp near the spiral start:
Checking if index 3 is >= 98 or <= 02 using unsigned wrap-around:
(uint8_t)(subqBuffer[3] - 0x03) >= 0xF5 (245) covers 0x98 to 0x02.
2. Tracking Maintenance:
- Valid sector (Mode 0x01 or Data) while already synchronized.
*/
/*
* HIT INCREMENT CONDITIONS:
* A. LEAD-IN PATTERNS: Detects TOC markers (A0-A2) or Track 01 at spiral start.
* (uint8_t)(subqBuffer[3] - 0x03) >= 0xF5 handles the 0x98 to 0x02 wrap-around.
* B. TRACKING LOCK: Maintains count if already synced and reading valid sectors.
*/
if ( (isDataSector && (pointAddress >= 0xA0 || (pointAddress == 0x01 && ( (uint8_t)(subqBuffer[3] - 0x03) >= 0xF5)))) ||
(currentHysteresis > 0 && (subqBuffer[0] == 0x01 || isDataSector)) )
{
@@ -324,10 +349,8 @@ void processLogic(uint8_t isDataSector) {
}
}
/*
DECREMENT CONDITION:
No valid match found. Slowly decrease confidence level.
*/
// --- STEP 2: Signal Decay / Missed Hits ---
// Reduce the hit counter if the current SUBQ sample fails validation.
if (currentHysteresis > 0) {
hysteresis = currentHysteresis - 1;
}
@@ -349,7 +372,7 @@ void processLogic(uint8_t isDataSector) {
* handles both speeds as it syncs directly to the signal edges.
*********************************************************************************************/
void performInjectionSequence(uint8_t injectSCEx) {
void PerformInjectionSequence(uint8_t injectSCEx) {
/*
Security strings (44-bit SCEx) for the three main regions:
0: NTSC-J (SCEI - Sony Computer Entertainment Inc.)
@@ -454,40 +477,30 @@ void performInjectionSequence(uint8_t injectSCEx) {
#if defined(PSNEE_DEBUG_SERIAL_MONITOR)
Debug_Inject();
DebugInject();
#endif
}
void Init() {
// --- Hardware Power & Peripheral Optimization ---
#if defined(ATmega328_168)
optimizePeripherals();
OptimizePeripherals();
#endif
#ifdef LED_RUN
PIN_LED_OUTPUT;
#endif
// --- Critical Boot Patching ---
#ifdef BIOS_PATCH
//uint8_t skipPatch = 0;
// #ifdef SCPH_7000
// // Check hardware switch for SCPH-7000 models
// PIN_SWITCH_INPUT;
// PIN_SWITCH_SET;
// if (PIN_SWITCH_READ == 0){
// skipPatch =1; // Disable patching if switch is triggered
// }
// #endif
// #ifdef LED_RUN
// PIN_LED_ON;
// #endif
// Execute BIOS patching unless bypassed by switch
// if (skipPatch == 0) {
Bios_Patching();
// }
// Execute BIOS patching
Bios_Patching();
// #ifdef LED_RUN
// PIN_LED_OFF;
@@ -497,6 +510,7 @@ void Init() {
PIN_SQCK_INPUT;
PIN_SUBQ_INPUT;
// --- Debug Interface Setup ---
#if defined(PSNEE_DEBUG_SERIAL_MONITOR) && defined(ATtiny85_45_25)
//pinMode(debugtx, OUTPUT); // software serial tx pin
mySerial.begin(115200); // 13,82 bytes in 12ms, max for softwareserial. (expected data: ~13 bytes / 12ms) // update: this is actually quicker
@@ -504,38 +518,38 @@ void Init() {
Serial.begin(500000); // 60 bytes in 12ms (expected data: ~26 bytes / 12ms) // update: this is actually quicker
#endif
// Detect board generation (PU-7 to PU-22+) before starting the main loop
board_detection();
}
// --- Console Analysis ---
// Identify board revision (PU-7 to PU-22+) to set correct injection timings
BoardBetection();
}
int main() {
Init();
while (1) {
// Small delay to prevent re-reading the tail end of the same SUBQ packet
// 1. Timing Sync: Prevent reading the tail end of the previous SUBQ packet
_delay_ms(1);
//Capture the 12-byte Sub-Q channel data into subqBuffer
captureSubQ();
// 2. Data Acquisition: Capture 12-byte SUBQ channel stream into buffer
CaptureSUBQ();
// Optimized Sector Filtering:
// 3. Sector Validation (Data/TOC check):
// Masking bits 7, 6, and 4 simultaneously using 0xD0 (binary 11010000).
// This verifies that the "Data/TOC" bit (0x40) is SET, while bits 7 and 4 are CLEARED.
// Equivalent to: (bit7 == 0 && bit6 == 1 && bit4 == 0).
//uint8_t isDataSector = ((scbuf[0] & 0xD0) == 0x40);
uint8_t isDataSector = ((subqBuffer[0] & 0xD0) == 0x40);
// Execute selected logic
processLogic(isDataSector);
FilterSUBQSamples(isDataSector);
//Trigger SCEx injection once the confidence threshold is reached
// 5. Execution: Trigger SCEx injection once confidence (hysteresis) is reached
if (hysteresis >= HYSTERESIS_MAX) {
performInjectionSequence(INJECT_SCEx);
PerformInjectionSequence(INJECT_SCEx);
}
}
return 0;
}

View File

@@ -1,27 +1,36 @@
#pragma once
/*
The _delay_us function uses loops to generate an approximate delay for the specified number of microseconds.
It calculates the number of clock cycles required to achieve the requested delay and loops the corresponding number of times.
The temporal precision of _delay_us depends on the microcontroller's clock frequency (F_CPU).
For the ATmega328 operating at a typical frequency of 16 MHz, here are some details on the precision.
*/
/*------------------------------------------------------------------------------------------------
Specific parameter section for BIOS patches
------------------------------------------------------------------------------------------------*/
* BIOS PATCH PARAMETERS EXPLANATION
*
* 1. SILENCE_THRESHOLD:
* Defines the size of a polling block where the AX pin must remain LOW.
* It serves as a "bus address call detector" for specific idle windows.
*
* 2. CONFIRM_COUNTER_TARGET:
* Defines the exact number of SILENCE_THRESHOLD blocks that must be validated
* consecutively before arming the hardware interrupt (ISR).
*
* 3. PULSE_COUNT:
* The starting value for the 'impulse' down-counter. It counts physical
* rising edges on the AX pin until it reaches zero to trigger the injection.
*
* 4. BIT_OFFSET_CYCLES (Inside ISR):
* The sub-microsecond delay (CPU cycles) applied after the final pulse to
* precisely align the DX injection with the target bit in the memory cycle.
*
* 5. OVERRIDE_CYCLES (Inside ISR):
* The duration for which the DX pin is forced to OUTPUT mode to overwrite
* the original BIOS data with the custom state.
------------------------------------------------------------------------------------------------*/
// tested with an Atmega328P
// ------ SCPH 100 / 102 ------
#if defined(SCPH_100) || \
defined(SCPH_102)
#define BIOS_PATCH
#define SILENCE_THRESHOLD 1500
#define CONFIRM_COUNTER_TARGET 8
@@ -53,8 +62,8 @@
#endif
// // ----- SCPH 5000 / 5500 -----
#ifdef SCPH_5000_5500
// // ----- SCPH 3500 / 5000 / 5500 -----
#ifdef SCPH_3500_5500
#define BIOS_PATCH
#define SILENCE_THRESHOLD 35600
#define CONFIRM_COUNTER_TARGET 1
@@ -63,30 +72,6 @@
#define OVERRIDE_CYCLES 3
#endif
// // // -------- SCPH 5000 --------
// #ifdef SCPH_5000
// #define BIOS_PATCH_3
// #define INTERRUPT_RISING
// #define SILENCE_THRESHOLD 35000
// #define CONFIRM_COUNTER_TARGET 1
// #define PULSE_COUNT 84
// #define BIT_OFFSET_CYCLES 60
// #define OVERRIDE_CYCLES 3
// #endif
// // #endif
// // -------- SCPH 3500 --------
#ifdef SCPH_3500
#define BIOS_PATCH
#define SILENCE_THRESHOLD 34000
#define CONFIRM_COUNTER_TARGET 1
#define PULSE_COUNT 85
#define BIT_OFFSET_CYCLES 44
#define OVERRIDE_CYCLES 3
#endif
// // -------- SCPH 3000 --------
#ifdef SCPH_3000
#define BIOS_PATCH
@@ -118,7 +103,7 @@
#define BIT_OFFSET_2_CYCLES 54
#define OVERRIDE_2_CYCLES 3
#endif
// #endif
/*------------------------------------------------------------------------------------------------
Region Settings Section
------------------------------------------------------------------------------------------------*/
@@ -126,7 +111,7 @@
#if defined(SCPH_100) || \
defined(SCPH_7500_9000) || \
defined(SCPH_7000) || \
defined(SCPH_5000_5500) || \
defined(SCPH_3500_5500) || \
defined(SCPH_3500) || \
defined(SCPH_3000) || \
defined(SCPH_1000) || \
@@ -156,39 +141,81 @@
serial debug section
------------------------------------------------------------------------------------------------*/
/******************************************************************************************
* FUNCTION : Debug_Log
*
* DESCRIPTION :
* Transmits hardware status and system configuration via the serial interface.
* Displays MCU frequency, WFCK detection mode, and active regional settings.
*
* - ATtiny: Uses a compact "Short-Code" format to minimize serial bus overhead.
* - Standard MCUs: Provides verbose, human-readable system diagnostics.
*
* INPUT : Wfck_mode (int) - The detected board generation (0: Static, 1: Frequency).
******************************************************************************************/
#if defined(PSNEE_DEBUG_SERIAL_MONITOR)
void Debug_Log (int Wfck_mode){ //Information about the MCU, and old or late console mode.
void Debug_Log (int Wfck_mode){
#if defined(ATtiny85_45_25)
mySerial.print("m "); mySerial.println(Wfck_mode);
#else
Serial.print(" MCU frequency: "); Serial.print(F_CPU); Serial.println(" Hz");
//Serial.print(" lows: "); Serial.println(Lows);
Serial.print(" wfck_mode: "); Serial.println(Wfck_mode);
Serial.print(" region: "); Serial.print(region[0]); Serial.print(region[1]); Serial.println(region[2]);
#endif
}
#if defined(ATtiny85_45_25)
// --- COMPACT SYSTEM LOG (ATtiny) ---
// Minimalistic output to save CPU cycles and maintain timing precision.
mySerial.print("m ");
mySerial.println(Wfck_mode); // Mode indicator
#else
// --- VERBOSE DIAGNOSTICS (ATmega) ---
// Detailed system information for standard development and debugging.
Serial.print(" MCU frequency: ");
Serial.print(F_CPU);
Serial.println(" Hz");
// Logs SUBQ packets to serial. We only have 12ms to write logs before the next packet.
// Slower MCUs (like ATtiny) receive minimal formatting to save cycles.
void logSubQ(uint8_t *dataBuffer) {
Serial.print(" wfck_mode: ");
Serial.println(Wfck_mode); // Board generation (0: Legacy, 1: Modern)
Serial.print(" region: ");
Serial.print(region[0]);
Serial.print(region[1]);
Serial.println(region[2]); // Active injection string (e.g., SCEE)
#endif
}
/******************************************************************************************
* FUNCTION : LogSUBQ
*
* DESCRIPTION :
* Logs captured SUBQ frames to the serial interface.
* Timing is critical: the entire 12-byte frame must be processed and transmitted
* within the ~12ms window before the next SUBQ packet arrives.
*
* - ATtiny: Uses minimal formatting (no spaces) to stay within the timing budget.
* - Standard MCUs: Includes spaces for better readability.
*
* INPUT : dataBuffer (uint8_t*) - Pointer to the 12-byte SUBQ frame.
******************************************************************************************/
void LogSUBQ(uint8_t *dataBuffer) {
// A bad sector read results in zeros (except for CRC). Skip logging if first 4 bytes are 0.
/**
* ERROR FILTERING:
* A failed sector read usually results in zeroed data (excluding CRC).
* Skip logging if the first 4 bytes are null to reduce bus traffic.
*/
if (!(dataBuffer[0] == 0 && dataBuffer[1] == 0 && dataBuffer[2] == 0 && dataBuffer[3] == 0)) {
#if defined(ATtiny85_45_25)
// Compact formatting for ATtiny to meet the 12ms timing constraint
// --- COMPACT FORMATTING (ATtiny) ---
// Minimal formatting to meet the strict 12ms timing constraint on slower MCUs.
for (uint8_t i = 0; i < 12; i++) {
if (dataBuffer[i] < 0x10) {
mySerial.print("0"); // Leading zero padding
mySerial.print("0"); // Leading zero padding for hex alignment
}
mySerial.print(dataBuffer[i], HEX);
}
mySerial.println("");
#else
// Standard formatting with spaces for more powerful MCUs
// --- STANDARD FORMATTING (ATmega) ---
// More descriptive output with space separation for easier debugging.
for (uint8_t i = 0; i < 12; i++) {
if (dataBuffer[i] < 0x10) {
Serial.print("0"); // Leading zero padding
@@ -202,66 +229,134 @@ void logSubQ(uint8_t *dataBuffer) {
}
}
void Debug_Inject(){ // Confirmation of region code injection
/******************************************************************************************
* FUNCTION : Debug_Inject
*
* DESCRIPTION :
* Provides real-time visual confirmation during SCEx region code injection.
* Used to verify that the hysteresis threshold has been met and the
* injection sequence is active.
*
* - ATtiny: Prints a single '!' to minimize CPU blocking during the critical
* injection timing window.
* - Standard MCUs: Prints a clear, verbose "INJECT !" message.
******************************************************************************************/
void DebugInject(){
#if defined(ATtiny85_45_25)
mySerial.print("!");
// --- MINIMALIST NOTIFICATION (ATtiny) ---
mySerial.print("!");
#else
// --- VERBOSE NOTIFICATION (ATmega) ---
// Standard visual feedback for debugging and monitoring.
Serial.println(" INJECT ! ");
#endif
}
#endif
/*------------------------------------------------------------------------------------------------
Compilation message
-----------------------------------------------------------------------------------------------*/
/*
* =============================================================
* COMPILATION CHECKS
* =============================================================
*/
#if !defined(SCPH_xxx3) && \
!defined(SCPH_102) && \
!defined(SCPH_101) && \
!defined(SCPH_100) && \
!defined(SCPH_7500_9000) && \
!defined(SCPH_7000) && \
!defined(SCPH_5000_5500) && \
!defined(SCPH_3500) && \
// --- CONSOLE SELECTION CHECK ---
#if !defined(SCPH_1000) && \
!defined(SCPH_3000) && \
!defined(SCPH_1000) && \
!defined(SCPH_5903) && \
!defined(SCPH_3500_5500) && \
!defined(SCPH_7000) && \
!defined(SCPH_7500_9000) && \
!defined(SCPH_100) && \
!defined(SCPH_101) && \
!defined(SCPH_102) && \
!defined(SCPH_xxx1) && \
!defined(SCPH_xxx2) && \
!defined(SCPH_xxx3) && \
!defined(SCPH_5903) && \
!defined(SCPH_xxxx)
#error "Console not selected! Please uncoment #define with SCPH model number."
#elif !defined(SCPH_xxx3) ^ \
defined(SCPH_102) ^ \
defined(SCPH_101) ^ \
defined(SCPH_100) ^ \
defined(SCPH_7500_9000) ^ \
defined(SCPH_7000) ^ \
defined(SCPH_5000_5500) ^ \
defined(SCPH_3500) ^ \
defined(SCPH_3000) ^ \
defined(SCPH_1000) ^ \
defined(SCPH_xxxx) ^ \
defined(SCPH_5903) ^ \
defined(SCPH_xxx1) ^ \
defined(SCPH_xxx2)
#error "May be selected only one console! Please check #define with SCPH model number."
#error "No console selected! Please uncomment your SCPH model."
#elif (defined(SCPH_1000) + \
defined(SCPH_3000) + \
defined(SCPH_3500_5500) + \
defined(SCPH_7000) + \
defined(SCPH_7500_9000) + \
defined(SCPH_100) + \
defined(SCPH_101) + \
defined(SCPH_102) + \
defined(SCPH_xxx1) + \
defined(SCPH_xxx2) + \
defined(SCPH_xxx3) + \
defined(SCPH_5903) + \
defined(SCPH_xxxx) > 1)
#error "Multiple consoles selected! Please enable only one SCPH model."
#endif
// --- MCU SELECTION CHECK ---
#if !defined(ATmega328_168) && \
!defined(ATmega32U4_16U4) && \
!defined(ATtiny85_45_25)
#error "MCU not selected! Please choose one"
#elif !defined(ATmega328_168) ^ \
defined(ATmega32U4_16U4 ) ^ \
defined(ATtiny85_45_25)
#error "May be selected only one MCU"
#error "No MCU selected! Please choose one supported architecture."
#elif (defined(ATmega328_168) + \
defined(ATmega32U4_16U4) + \
defined(ATtiny85_45_25) > 1)
#error "Multiple MCUs selected! Please enable only one architecture."
#endif
#if defined(LED_RUN) && \
defined(PSNEE_DEBUG_SERIAL_MONITOR) && \
defined(ATtiny85_45_25)
#error"Compilation options LED_RUN and PSNEE_DEBUG_SERIAL_MONITOR are not simultaneously compatible with ATtiny85_45_25"
// --- RESOURCE CONFLICT CHECK (ATtiny) ---
#if defined(ATtiny85_45_25) && \
defined(LED_RUN) && \
defined(PSNEE_DEBUG_SERIAL_MONITOR)
#error "Resource conflict: LED_RUN and DEBUG_SERIAL are incompatible on ATtiny."
#endif
// --- Console Model Info ---
#if defined(SCPH_1000)
#pragma message "Target Console: SCPH-1000 (NTSC-J)"
#elif defined(SCPH_3000)
#pragma message "Target Console: SCPH-3000 (NTSC-J)"
#elif defined(SCPH_3500_5500)
#pragma message "Target Console: SCPH-3500/5000/5500 (NTSC-J)"
#elif defined(SCPH_7000)
#pragma message "Target Console: SCPH-7000 (Internal Switch enabled)"
#elif defined(SCPH_7500_9000)
#pragma message "Target Console: SCPH-7500/9000 (NTSC-J)"
#elif defined(SCPH_100)
#pragma message "Target Console: SCPH-100 (NTSC-J)"
#elif defined(SCPH_101)
#pragma message "Target Console: SCPH-101 (NTSC-U/C)"
#elif defined(SCPH_102)
#pragma message "Target Console: SCPH-102 (PAL)"
#elif defined(SCPH_xxx1)
#pragma message "Target Console: Generic NTSC-U/C"
#elif defined(SCPH_xxx2)
#pragma message "Target Console: Generic PAL"
#elif defined(SCPH_xxx3)
#pragma message "Target Console: Generic NTSC-J"
#elif defined(SCPH_5903)
#pragma message "Target Console: SCPH-5903 (Video CD Dual-Interface)"
#elif defined(SCPH_xxxx)
#pragma message "Target Console: Universal Region Mode"
#endif
// --- MCU Architecture Info ---
#if defined(ATmega328_168)
#pragma message "Microcontroller: ATmega328/168 (Arduino Nano/Uno)"
#elif defined(ATmega32U4_16U4)
#pragma message "Microcontroller: ATmega32U4/16U4 (Leonardo/Pro Micro)"
#elif defined(ATtiny85_45_25)
#pragma message "Microcontroller: ATtiny85/45/25"
#endif
// --- Feature Status ---
#ifdef PSNEE_DEBUG_SERIAL_MONITOR
#pragma message "Feature: Serial Debug Monitor ENABLED"
#endif
#ifdef LED_RUN
#pragma message "Feature: Status LED (PB5) ENABLED"
#endif