1
0
mirror of https://github.com/kalymos/PsNee.git synced 2026-05-07 08:06:39 +00:00

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>
This commit is contained in:
kalymos
2026-04-19 19:31:47 +02:00
committed by GitHub
parent b4d72e2539
commit 0d2290aa68
21 changed files with 2156 additions and 19 deletions

1032
PSNee/MCU.h Normal file

File diff suppressed because it is too large Load Diff

740
PSNee/PSNee.ino Normal file
View 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
View 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

View File

@@ -1,25 +1,41 @@
# PSNee V8.6
THE modechip supports the largest number of Playstation 1 variants, and the largest number of microcontrollers
# For all useful information consult the ![Wiki](https://github.com/kalymos/PsNee/wiki)
# PSNee V9.0
PSNee is compatible with all retail PlayStation 1 models and various AVR microcontrollers.
![Logo](/images/PSNee-8.6-logo.png)
## For all useful information, please consult the [Wiki](https://github.com).
![Logo](/images/PSNee-9.0-logo.png)
## Features
- Remove Disk Region Protection
- Patch BIOS
- A specific library for card support, to solve the fuse setting problem.
- The mode does not take care of changing PAL <-> NTSC video output (in other words if you use a Japanese console and you put European games, or in older European models you use American or Japanese games... the display will not be correct)
- **Disc Region Protection:** Removes region locks.
- **BIOS Patching:** Provides enhanced compatibility.
- **Board Support:** Custom boards manager to simplify fuse settings and hardware configuration.
## Supported Playstation 1
All US models, all European models, and the vast majority of Japanese models.
> [!IMPORTANT]
> **Video Signal Note:** This modchip does not handle PAL/NTSC video signal conversion. Playing games from a different region may result in an incorrect display (e.g., NTSC games on early PAL consoles, or PAL games on NTSC-U/C or NTSC-J consoles).
>
> European models from SCPH-5502 to SCPH-9002 typically handle NTSC signals correctly. However, **SCPH-102** requires a BIOS patch to support it.
>
> See the Example of a distorted image at the bottom of the page.
## Supported platforms
- ATmega328(A/P/PA) @16Mhz
- ATmega168(A/P/PA) @16Mhz
- Atmega32U4 @16Mhz
- ATtiny25/45/85 @8Mhz no BIOS patch!
## Supported Hardware (AVR)
- **ATmega328(A/P/PA)** @16MHz
- **ATmega168(A/P/PA)** @16MHz
- **ATmega32U4** @16MHz
- **ATtiny25/45/85** @8MHz *(No BIOS patch support!)*
> [!CAUTION]
> **SCPH-7000 NTSF J (BIOS Patch & Memory Cards):**
> On SCPH-7000 models, the BIOS patch has an issue with memory cards. The only way to bypass it is to install a switch on the mod to disable the BIOS patch. This is done according to the available diagram.
---
### Example of a distorted image
*Typical result when playing a game from a different region without a signal converter.*
![gray](images/issue/gray-screens.png)
### Verified Hardware (Personal Test Collection)
![test](images/test-mat.png)
*From left to right: NTSC-J, PAL, and NTSC-U/C & Asia models.*
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/B0B81WGP0Z)
## Model that I personally tested
![test](images/test-PSNee-v8.6.png)
Example of gray image
![gray](https://github.com/kalymos/PsNee/blob/master/images/issue/gray-screens.png)

View File

@@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 MiB

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 MiB

After

Width:  |  Height:  |  Size: 4.5 MiB

BIN
images/BIOS/32p.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 981 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
images/BIOS/40p.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

View File

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

BIN
images/PSNee-9.0-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 383 KiB

After

Width:  |  Height:  |  Size: 8.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 358 KiB

After

Width:  |  Height:  |  Size: 2.7 MiB