Merge V9.0 into Main (#111)
* MUC-MCU Fixed the MUC-MCU file name. Fixed a bug that prevented additional MCUs from being compiled. Modified the clock registers for ATtiny 48-88s; the TCCROB register is missing in these MUCs. * Too many things to summarize Refactoring of the setting file, to be able to have settings specifying the HYSTERESIS_MAX variable. Renamed precompilation variable in BIOS_patching file. Changing console selection in .ino file * Renaming files to make updating easier * Update README.md * logo 8.7 * optimization on regin injection * Ad bios patch 102A, and correct some images. * SCPH_102A bios patch WIP * Cleaning the selection area Redesign of the selection area, and addition of the SCPH_hyma mode equivalent to the legacy mode with the difference of a hysteresis of 25 * Layout modification * clining * ad debug mode * debug mesga * creation of debug functions, and cleaning * Update PSNee.ino * serial debug cleaning * added compilation message * some comment cleaning * removal of the TIMER removal of the ISR(CTC_TIMER_VECTOR) function, and all its dependencies. - Timer_Start - Timer_Stop modification - Board detection - inject_SCEX - BIOS_PATCH - MCU - Setting * Update PSNee.ino Implementing a dedicated board_detection function with optimized logic. Remodeling the inject_SCEX function. * timer optimization * Breaking down the main function into smaller modules. Modular breakdown: Separated main logic into - captureSubQ, - logic_SCPH_5903, - logic_Standard, - performInjectionSequence. Added support for SCPH-5903 by RepairBox Co-Authored-By: RepairBox <33960601+danielfergisz@users.noreply.github.com> * Optimization performInjectionSequence(): Deep refactor with a cleaner. captureSubQ(): Faster bit-banging and improved timing accuracy. board_detection(): Optimized logic . General Cleanup: Fixed all warnings and minimized memory footprint. * Optimization Optimization of the performInjectionSequence function BIOS patch verification (work in progress) and general code cleanup * BIOS patch obtimisation * opti patch bios 3500 * opti bios patch SCPH-1000 * opti BIOS patch SCPH-3000, SCPH-100. * refactor: optimize BIOS patching sequence with refined timing nomenclature Refined the BIOS patching and standardized timing nomenclature for better technical clarity. Changes: - Ad technical documentation within the code. - Standardized timing variables for better clarity: - BOOT_OFFSET: Replaces initial checkpoint. - FOLLOWUP_OFFSET: Replaces initial checkpoint2. - PULSE_COUNT: Replaces trigger . - BIT_OFFSET: Replaces hold . - OVERRIDE: Replaces patching. * test refactor: optimize PS1 patch for SCPH-100 using zero-latency polling Ported the BIOS patch from ISR to manual polling to achieve cycle-accurate precision for 33.86MHz bus timing. * test neo BIOS patch * Refactor pulse counting logic for jitter reduction * BIOS patch simplification Removal of ISR and reduction of code in the BIOS patch, for improved portability and robustness. For now it is only available for the SCPH-100 and 102. * BIOS patche neo for SCPH-9000_7500 SCPH-7000 SCPH-5500 * BIOS patch neo SCPH-5000 SCPH-3500 * BIOS patche neo SCPH-3000 SCPH-1000 I finally completed the value conversion for the BIOS patch without IRS * refactor: optimize core detection and injection logic for performance and size - board_detection: Added early exit for newer boards and tightened the sampling loop to reduce CPU overhead. - captureSubQ: Simplified clock-edge synchronization and localized buffers to improve register allocation. - performInjectionSequence: Replaced bit-calculation math with a streamlined byte-shift approach. Refined WFCK modulation to ensure zero-jitter phase locking. - logic_Standard/5903: Refactored pattern matching into a decision tree. Factorized redundant buffer checks (scbuf[1]/[6]) and replaced multiple equality tests with range comparisons. Optimization results: - Reduced Flash memory footprint (smaller binary size). - Improved execution speed by removing redundant logical operations. - Increased signal stability during the critical injection phase. * PSNee v9.0 Update New PSNeeCore v2: Completely rewritten core engine for all supported MCUs. Auto-MCU Detection: Manual board selection removed. Automatically detects ATmega (328/168/128/PB), 32U4, and ATtiny families at compile time. Deep Init Optimization: Hard-shutdown of unused internal modules (ADC, DAC, Timers) to minimize power draw and electrical noise. Native PB Support: Full compatibility for 128PB and 328PB (PRR2 registers and extra Ports). * PSNee v9.0 Update New PSNeeCore v2: Completely rewritten core engine for all supported MCUs. Auto-MCU Detection: Manual board selection removed. Automatically detects ATmega (328/168/128/PB), 32U4, and ATtiny families at compile time. Deep Init Optimization: Hard-shutdown of unused internal modules (ADC, DAC, Timers) to minimize power draw and electrical noise. Native PB Support: Full compatibility for 128PB and 328PB (PRR2 registers and extra Ports). logo --------- Co-authored-by: RepairBox <33960601+danielfergisz@users.noreply.github.com>
1032
PSNee/MCU.h
Normal file
740
PSNee/PSNee.ino
Normal file
@@ -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;
|
||||
}
|
||||
333
PSNee/settings.h
Normal file
@@ -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
|
||||
|
||||
|
||||
54
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 
|
||||
# PSNee V9.0
|
||||
PSNee is compatible with all retail PlayStation 1 models and various AVR microcontrollers.
|
||||
|
||||

|
||||
## For all useful information, please consult the [Wiki](https://github.com).
|
||||
|
||||

|
||||
|
||||
## 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.*
|
||||

|
||||
|
||||
### Verified Hardware (Personal Test Collection)
|
||||

|
||||
*From left to right: NTSC-J, PAL, and NTSC-U/C & Asia models.*
|
||||
|
||||
[](https://ko-fi.com/B0B81WGP0Z)
|
||||
|
||||
## Model that I personally tested
|
||||

|
||||
Example of gray image
|
||||

|
||||
|
||||
16
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
|
||||
|
||||
|
Before Width: | Height: | Size: 5.2 MiB |
|
Before Width: | Height: | Size: 5.2 MiB |
|
Before Width: | Height: | Size: 5.1 MiB After Width: | Height: | Size: 4.8 MiB |
|
Before Width: | Height: | Size: 5.2 MiB After Width: | Height: | Size: 4.5 MiB |
BIN
images/BIOS/32p.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 981 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.1 MiB |
BIN
images/BIOS/40p.png
Normal file
|
After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 1.6 MiB |
BIN
images/PSNee-9.0-logo.png
Normal file
|
After Width: | Height: | Size: 3.0 MiB |
|
Before Width: | Height: | Size: 100 KiB |
BIN
images/my test material/test-mat.png
Normal file
|
After Width: | Height: | Size: 524 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 383 KiB After Width: | Height: | Size: 8.8 MiB |
|
Before Width: | Height: | Size: 358 KiB After Width: | Height: | Size: 2.7 MiB |