mirror of
https://github.com/kalymos/PsNee.git
synced 2026-05-09 00:32:52 +00:00
Debug: Fix 32U4 Serial1 & Refactor Bios_Patching
Debug: Fix Serial output for ATmega32U4 (Serial1) Refactor: Move Bios_Patching logic to PSNee.ino Cleanup: Remove obsolete Bios_patching.h
This commit is contained in:
@@ -1,337 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* ======================================================================================
|
||||
* FUNCTION : Bios_Patching()
|
||||
* TARGET : Data Bus (DX) synchronized via Address Bus (AX / AY)
|
||||
*
|
||||
* OPERATIONAL LOGIC:
|
||||
* Intercepts specific memory transactions by monitoring address lines (AX/AY).
|
||||
* Uses hardware interrupts (ISR) for high-speed pulse counting and cycle-accurate
|
||||
* delays to inject modified data onto the Data line (DX) in real-time.
|
||||
*
|
||||
* KEY PHASES:
|
||||
* 1. STABILIZATION & ALIGNMENT (AX): Synchronizes execution with a clean rising
|
||||
* edge of the AX signal to establish a deterministic timing reference.
|
||||
*
|
||||
* 2. ADDRESS CALL DETECTION (PHASE 2): Scans the bus for specific address calls
|
||||
* by validating consecutive polling blocks (SILENCE_THRESHOLD).
|
||||
*
|
||||
* 3. SILENCE CONFIRMATION (GATING): Counts the exact number of validated silence
|
||||
* windows (CONFIRM_COUNTER_TARGET) to reach the correct pre-patching stage.
|
||||
*
|
||||
* 4. HARDWARE PULSE COUNTING (ISR): Uses INT0/INT1 to decrement the 'impulse'
|
||||
* counter on each rising edge with minimal hardware latency.
|
||||
*
|
||||
* 5. DATA OVERDRIVE (DX): Upon reaching the target pulse, triggers a calibrated
|
||||
* delay (BIT_OFFSET) and momentarily forces the DX pin to OUTPUT mode to
|
||||
* overwrite the BIOS bit for a precise duration (OVERRIDE).
|
||||
* ======================================================================================
|
||||
*/
|
||||
|
||||
#ifdef BIOS_PATCH
|
||||
|
||||
/**
|
||||
* 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 (SCPH-7000 specific) ---
|
||||
#if defined(SCPH_7000)
|
||||
PIN_SWITCH_INPUT; // Configure Pin D5 as Input
|
||||
PIN_SWITCH_SET; // Enable internal Pull-up (D5 defaults to HIGH)
|
||||
__builtin_avr_delay_cycles(10); // Short delay for voltage stabilization
|
||||
|
||||
/**
|
||||
* Exit immediately if the switch pulls the pin to GND (Logic LOW).
|
||||
* This allows the user to disable the BIOS patch on-the-fly.
|
||||
*/
|
||||
if (PIN_SWITCH_READ == 0) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
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 ---
|
||||
// Align execution pointer to a known rising edge state.
|
||||
if (PIN_AX_READ != 0) {
|
||||
while (WAIT_AX_FALLING); // Wait if bus is busy
|
||||
while (WAIT_AX_RISING); // Sync with next pulse start
|
||||
} else {
|
||||
while (WAIT_AX_RISING); // Sync with upcoming pulse
|
||||
}
|
||||
|
||||
// --- 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
|
||||
|
||||
|
||||
/*
|
||||
* Portability Note: The non-ISR (polling-based) version of the code is maintained
|
||||
* to facilitate porting to other platforms and architectures that may not support
|
||||
* AVR-specific hardware interrupts.
|
||||
*
|
||||
* Stability Limitation: While the polling version is more portable, it was found
|
||||
* that at 16MHz, achieving consistent DATA OVERDRIVE stability is nearly impossible
|
||||
* without using a Hardware ISR. The latency and jitter of software polling at this
|
||||
* frequency are too high to guarantee a sub-microsecond cycle-accurate injection.
|
||||
* Therefore, for ATmega328P @ 16MHz, the ISR-driven implementation remains the
|
||||
* tandard.
|
||||
*/
|
||||
|
||||
// #ifdef BIOS_PATCH
|
||||
|
||||
// volatile uint8_t impulse = 0;
|
||||
// volatile uint8_t patch = 0;
|
||||
|
||||
|
||||
// ISR(PIN_AX_INTERRUPT_VECTOR) {
|
||||
// //impulse--;
|
||||
// if (--impulse == 0){ // If impulse reaches the value defined by TRIGGER, the following actions are performed:
|
||||
// // Precise cycle-accurate delay before triggering
|
||||
// __builtin_avr_delay_cycles(BIT_OFFSET_CYCLES);
|
||||
|
||||
// #ifdef PHASE_TWO_PATCH
|
||||
// PIN_DX_SET;
|
||||
// #endif
|
||||
|
||||
// PIN_DX_OUTPUT; // Pull the line (Override start)
|
||||
// __builtin_avr_delay_cycles(OVERRIDE_CYCLES);
|
||||
|
||||
// #ifdef PHASE_TWO_PATCH
|
||||
// PIN_DX_CLEAR; // Release the bus (Override end)
|
||||
// #endif
|
||||
|
||||
// PIN_DX_INPUT;
|
||||
|
||||
// PIN_LED_OFF;
|
||||
// PIN_AX_INTERRUPT_DISABLE;
|
||||
// patch = 1; // patch is set to 1, indicating that the first patch is completed.
|
||||
// }
|
||||
// }
|
||||
|
||||
// #ifdef PHASE_TWO_PATCH
|
||||
|
||||
|
||||
// ISR(PIN_AY_INTERRUPT_VECTOR){
|
||||
|
||||
// //impulse--;
|
||||
// if (--impulse == 0) // If impulse reaches the value defined by TRIGGER2, the following actions are performed:
|
||||
// {
|
||||
// __builtin_avr_delay_cycles(BIT_OFFSET_2_CYCLES);
|
||||
|
||||
// PIN_DX_OUTPUT;
|
||||
// __builtin_avr_delay_cycles(OVERRIDE_2_CYCLES);
|
||||
// PIN_DX_INPUT;
|
||||
|
||||
// PIN_AY_INTERRUPT_DISABLE;
|
||||
// PIN_LED_OFF;
|
||||
// patch = 2; // patch is set to 2, indicating that the second patch is completed.
|
||||
// }
|
||||
// }
|
||||
// #endif
|
||||
|
||||
// // --- BIOS Patching Main Function ---
|
||||
// void Bios_Patching(void) {
|
||||
|
||||
// uint8_t current_confirms = 0;
|
||||
// uint8_t count = 0;
|
||||
// patch = 0;
|
||||
// sei();
|
||||
// PIN_AX_INPUT;
|
||||
// // --- PHASE 1: Signal Stabilization & Alignment (AX) ---
|
||||
// if (PIN_AX_READ != 0) {
|
||||
// while (WAIT_AX_FALLING); // Wait for falling edge
|
||||
// while (WAIT_AX_RISING); // Wait for next rising edge to sync
|
||||
// } else {
|
||||
// while (WAIT_AX_RISING); // Wait for first rising edge
|
||||
// }
|
||||
|
||||
// // --- PHASE 2: Silence Detection (AX) ---
|
||||
|
||||
// while (current_confirms < CONFIRM_COUNTER_TARGET) {
|
||||
// uint16_t count = SILENCE_THRESHOLD;
|
||||
|
||||
// // --- Scan for ONE continuous block of silence ---
|
||||
// while (count > 0) {
|
||||
// if (PIN_AX_READ != 0) {
|
||||
// while (WAIT_AX_FALLING); // Pulse detected: wait for bus to clear
|
||||
// break; // Reset and try a new silence block
|
||||
// }
|
||||
// count--;
|
||||
// }
|
||||
|
||||
// // If count reaches 0, a silent block is validated
|
||||
// if (count == 0) {
|
||||
// current_confirms++;
|
||||
// }
|
||||
// }
|
||||
// impulse = PULSE_COUNT;
|
||||
// PIN_LED_ON;
|
||||
// PIN_AX_INTERRUPT_RISING;
|
||||
// PIN_AX_INTERRUPT_ENABLE;
|
||||
// while (patch != 1); // Wait for the first stage of the patch to complete:
|
||||
|
||||
// //PIN_LED_OFF;
|
||||
// // -------- Secondary Patch ----------
|
||||
// #ifdef PHASE_TWO_PATCH
|
||||
|
||||
// current_confirms = 0;
|
||||
// while (current_confirms < CONFIRM_COUNTER_TARGET_2) {
|
||||
// uint16_t count = SILENCE_THRESHOLD;
|
||||
|
||||
// while (count > 0) {
|
||||
// if (PIN_AX_READ != 0) {
|
||||
|
||||
// while (WAIT_AX_FALLING);
|
||||
// break;
|
||||
// }
|
||||
// count--;
|
||||
// }
|
||||
|
||||
// if (count == 0) {
|
||||
// current_confirms++;
|
||||
// }
|
||||
// }
|
||||
|
||||
// PIN_LED_ON;
|
||||
// impulse = PULSE_COUNT_2;
|
||||
// PIN_AY_INTERRUPT_RISING;
|
||||
// PIN_AY_INTERRUPT_ENABLE;
|
||||
|
||||
// while (patch != 2); // Wait for the second stage of the patch to complete:
|
||||
|
||||
// #endif
|
||||
|
||||
// cli();
|
||||
// }
|
||||
// #endif
|
||||
|
||||
|
||||
146
PSNee/MCU.h
146
PSNee/MCU.h
@@ -298,7 +298,7 @@
|
||||
// 3. Digital Input Buffer Disable (DIDR0 & DIDR2)
|
||||
// 32U4 has more analog channels (ADC0-ADC7 and ADC8-ADC13)
|
||||
DIDR0 = 0xFF; // Disable digital buffers on F0-F7
|
||||
DIDR2 = 0x3F; // Disable digital buffers on D4, D6, D7, B4, B5, B6
|
||||
//DIDR2 = 0x3F; // Disable digital buffers on D4, D6, D7, B4, B5, B6
|
||||
|
||||
// 4. GPIO Strategy (Unused pins to Pull-up)
|
||||
// On 32U4, Port C is small (only PC6/PC7). Adjusting to cover most unused pins.
|
||||
@@ -318,7 +318,7 @@
|
||||
PRR1 = (1 << PRUSB) | // Disable USB Controller (Stops SOF interrupts)
|
||||
(1 << PRTIM3) | // Timer 3 Off
|
||||
(1 << 4) | // Timer 4 Off (High speed timer)
|
||||
(1 << PRUSART1); // Disable Serial1 (UART)
|
||||
(0 << PRUSART1); // KEEP SERIAL1 ACTIVE (PD1/TX1) - Must be 0
|
||||
|
||||
// 6. Double Security for Timer 0 (Redundancy)
|
||||
TCCR0B = 0;
|
||||
@@ -858,4 +858,146 @@
|
||||
#endif // BIOS Patching
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Portability Note: The non-ISR (polling-based) version of the code is maintained
|
||||
* to facilitate porting to other platforms and architectures that may not support
|
||||
* AVR-specific hardware interrupts.
|
||||
*
|
||||
* Stability Limitation: While the polling version is more portable, it was found
|
||||
* that at 16MHz, achieving consistent DATA OVERDRIVE stability is nearly impossible
|
||||
* without using a Hardware ISR. The latency and jitter of software polling at this
|
||||
* frequency are too high to guarantee a sub-microsecond cycle-accurate injection.
|
||||
* Therefore, for ATmega328P @ 16MHz, the ISR-driven implementation remains the
|
||||
* tandard.
|
||||
*/
|
||||
|
||||
// #ifdef BIOS_PATCH
|
||||
|
||||
// volatile uint8_t impulse = 0;
|
||||
// volatile uint8_t patch = 0;
|
||||
|
||||
|
||||
// ISR(PIN_AX_INTERRUPT_VECTOR) {
|
||||
// //impulse--;
|
||||
// if (--impulse == 0){ // If impulse reaches the value defined by TRIGGER, the following actions are performed:
|
||||
// // Precise cycle-accurate delay before triggering
|
||||
// __builtin_avr_delay_cycles(BIT_OFFSET_CYCLES);
|
||||
|
||||
// #ifdef PHASE_TWO_PATCH
|
||||
// PIN_DX_SET;
|
||||
// #endif
|
||||
|
||||
// PIN_DX_OUTPUT; // Pull the line (Override start)
|
||||
// __builtin_avr_delay_cycles(OVERRIDE_CYCLES);
|
||||
|
||||
// #ifdef PHASE_TWO_PATCH
|
||||
// PIN_DX_CLEAR; // Release the bus (Override end)
|
||||
// #endif
|
||||
|
||||
// PIN_DX_INPUT;
|
||||
|
||||
// PIN_LED_OFF;
|
||||
// PIN_AX_INTERRUPT_DISABLE;
|
||||
// patch = 1; // patch is set to 1, indicating that the first patch is completed.
|
||||
// }
|
||||
// }
|
||||
|
||||
// #ifdef PHASE_TWO_PATCH
|
||||
|
||||
|
||||
// ISR(PIN_AY_INTERRUPT_VECTOR){
|
||||
|
||||
// //impulse--;
|
||||
// if (--impulse == 0) // If impulse reaches the value defined by TRIGGER2, the following actions are performed:
|
||||
// {
|
||||
// __builtin_avr_delay_cycles(BIT_OFFSET_2_CYCLES);
|
||||
|
||||
// PIN_DX_OUTPUT;
|
||||
// __builtin_avr_delay_cycles(OVERRIDE_2_CYCLES);
|
||||
// PIN_DX_INPUT;
|
||||
|
||||
// PIN_AY_INTERRUPT_DISABLE;
|
||||
// PIN_LED_OFF;
|
||||
// patch = 2; // patch is set to 2, indicating that the second patch is completed.
|
||||
// }
|
||||
// }
|
||||
// #endif
|
||||
|
||||
// // --- BIOS Patching Main Function ---
|
||||
// void Bios_Patching(void) {
|
||||
|
||||
// uint8_t current_confirms = 0;
|
||||
// uint8_t count = 0;
|
||||
// patch = 0;
|
||||
// sei();
|
||||
// PIN_AX_INPUT;
|
||||
// // --- PHASE 1: Signal Stabilization & Alignment (AX) ---
|
||||
// if (PIN_AX_READ != 0) {
|
||||
// while (WAIT_AX_FALLING); // Wait for falling edge
|
||||
// while (WAIT_AX_RISING); // Wait for next rising edge to sync
|
||||
// } else {
|
||||
// while (WAIT_AX_RISING); // Wait for first rising edge
|
||||
// }
|
||||
|
||||
// // --- PHASE 2: Silence Detection (AX) ---
|
||||
|
||||
// while (current_confirms < CONFIRM_COUNTER_TARGET) {
|
||||
// uint16_t count = SILENCE_THRESHOLD;
|
||||
|
||||
// // --- Scan for ONE continuous block of silence ---
|
||||
// while (count > 0) {
|
||||
// if (PIN_AX_READ != 0) {
|
||||
// while (WAIT_AX_FALLING); // Pulse detected: wait for bus to clear
|
||||
// break; // Reset and try a new silence block
|
||||
// }
|
||||
// count--;
|
||||
// }
|
||||
|
||||
// // If count reaches 0, a silent block is validated
|
||||
// if (count == 0) {
|
||||
// current_confirms++;
|
||||
// }
|
||||
// }
|
||||
// impulse = PULSE_COUNT;
|
||||
// PIN_LED_ON;
|
||||
// PIN_AX_INTERRUPT_RISING;
|
||||
// PIN_AX_INTERRUPT_ENABLE;
|
||||
// while (patch != 1); // Wait for the first stage of the patch to complete:
|
||||
|
||||
// //PIN_LED_OFF;
|
||||
// // -------- Secondary Patch ----------
|
||||
// #ifdef PHASE_TWO_PATCH
|
||||
|
||||
// current_confirms = 0;
|
||||
// while (current_confirms < CONFIRM_COUNTER_TARGET_2) {
|
||||
// uint16_t count = SILENCE_THRESHOLD;
|
||||
|
||||
// while (count > 0) {
|
||||
// if (PIN_AX_READ != 0) {
|
||||
|
||||
// while (WAIT_AX_FALLING);
|
||||
// break;
|
||||
// }
|
||||
// count--;
|
||||
// }
|
||||
|
||||
// if (count == 0) {
|
||||
// current_confirms++;
|
||||
// }
|
||||
// }
|
||||
|
||||
// PIN_LED_ON;
|
||||
// impulse = PULSE_COUNT_2;
|
||||
// PIN_AY_INTERRUPT_RISING;
|
||||
// PIN_AY_INTERRUPT_ENABLE;
|
||||
|
||||
// while (patch != 2); // Wait for the second stage of the patch to complete:
|
||||
|
||||
// #endif
|
||||
|
||||
// cli();
|
||||
// }
|
||||
// #endif
|
||||
|
||||
|
||||
|
||||
|
||||
210
PSNee/PSNee.ino
210
PSNee/PSNee.ino
@@ -46,7 +46,7 @@
|
||||
// D13 for Arduino, ATtiny add a led between PB3 (pin 2) and gnd with a 1k resistor in series,
|
||||
// ATmega32U4 (Pro Micro) add a led between PB6 (pin 10) and gnd with a 1k resistor in series.
|
||||
|
||||
#define DEBUG_SERIAL_MONITOR // Enables serial monitor output.
|
||||
// #define DEBUG_SERIAL_MONITOR // Enables serial monitor output.
|
||||
|
||||
/******************************************************************************************************************
|
||||
* Requires compilation with Arduino libs!
|
||||
@@ -105,8 +105,6 @@
|
||||
|
||||
#include "MCU.h"
|
||||
#include "settings.h"
|
||||
#include "BIOS_patching.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
|
||||
@@ -124,8 +122,202 @@ uint8_t hysteresis = 0;
|
||||
/*******************************************************************************************************************
|
||||
* 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 (SCPH-7000 specific) ---
|
||||
#if defined(SCPH_7000)
|
||||
PIN_SWITCH_INPUT; // Configure Pin D5 as Input
|
||||
PIN_SWITCH_SET; // Enable internal Pull-up (D5 defaults to HIGH)
|
||||
__builtin_avr_delay_cycles(10); // Short delay for voltage stabilization
|
||||
|
||||
/**
|
||||
* Exit immediately if the switch pulls the pin to GND (Logic LOW).
|
||||
* This allows the user to disable the BIOS patch on-the-fly.
|
||||
*/
|
||||
if (PIN_SWITCH_READ == 0) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
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
|
||||
*
|
||||
@@ -390,7 +582,7 @@ void PerformInjectionSequence(uint8_t injectSCEx) {
|
||||
|
||||
const uint16_t BIT_DELAY = 4000; // 4000us is the standard bit timing for SCEx signal (approx. 250 bps)
|
||||
const uint8_t isWfck = wfck_mode; // Cache wfck_mode to save CPU cycles during the bit loop
|
||||
hysteresis = (HYSTERESIS_MAX - 10); // Reset hysteresis to mid-point after triggering
|
||||
hysteresis = (HYSTERESIS_MAX - 6); // Reset hysteresis to mid-point after triggering
|
||||
|
||||
#ifdef LED_RUN
|
||||
PIN_LED_ON;
|
||||
@@ -524,11 +716,15 @@ void Init() {
|
||||
// --- 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. (expected data: ~13 bytes / 12ms) // update: this is actually quicker
|
||||
#elif defined(DEBUG_SERIAL_MONITOR) && !defined(IS_ATTINY_FAMILY)
|
||||
Serial.begin(500000); // 60 bytes in 12ms (expected data: ~26 bytes / 12ms) // update: this is actually quicker
|
||||
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
|
||||
|
||||
|
||||
// --- Console Analysis ---
|
||||
// Identify board revision (PU-7 to PU-22+) to set correct injection timings
|
||||
BoardDetection();
|
||||
|
||||
@@ -217,6 +217,12 @@
|
||||
#if defined(DEBUG_SERIAL_MONITOR)
|
||||
extern uint8_t hysteresis;
|
||||
|
||||
#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)
|
||||
@@ -228,12 +234,13 @@ void BoardDetectionLog (uint16_t window_result, uint8_t Wfck_mode, uint8_t regio
|
||||
"PAL", // 2
|
||||
"Universal" // 3
|
||||
};
|
||||
Serial.println(" ");
|
||||
Serial.print(F(" MCU frequency: ")); Serial.print(F_CPU / 1000000L); Serial.println(F(" MHz"));
|
||||
Serial.print(F(" Window remaining: ")); Serial.println(window_result); // Real-time diagnostic
|
||||
Serial.print(" wfck_mode: "); Serial.println(Wfck_mode);
|
||||
Serial.print(" region: "); Serial.print(regionNames[region]);
|
||||
Serial.println(" ");
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
@@ -258,7 +265,7 @@ void CaptureSUBQLog(uint8_t *dataBuffer) {
|
||||
#if defined(IS_ATTINY_FAMILY)
|
||||
mySerial.print(F(" [Err x")); mySerial.print(errorCount); mySerial.println(F("]"));
|
||||
#else
|
||||
Serial.print(F(" [Missed sectors: ")); Serial.print(errorCount); Serial.println(F("]"));
|
||||
DEBUG_OUT.print(F(" [Missed sectors: ")); DEBUG_OUT.print(errorCount); DEBUG_OUT.println(F("]"));
|
||||
#endif
|
||||
errorCount = 0; // Reset counter after reporting
|
||||
}
|
||||
@@ -278,13 +285,13 @@ void CaptureSUBQLog(uint8_t *dataBuffer) {
|
||||
// Formatted hex output for ATmega
|
||||
for (uint8_t i = 0; i < 12; i++) {
|
||||
uint8_t val = dataBuffer[i];
|
||||
if (val < 0x10) Serial.print("0");
|
||||
Serial.print(val, HEX);
|
||||
Serial.print(" ");
|
||||
if (val < 0x10) DEBUG_OUT.print("0");
|
||||
DEBUG_OUT.print(val, HEX);
|
||||
DEBUG_OUT.print(" ");
|
||||
}
|
||||
// Append global hysteresis on the same line
|
||||
Serial.print(F("| Hyst: "));
|
||||
Serial.println(hysteresis);
|
||||
DEBUG_OUT.print(F("| Hyst: "));
|
||||
DEBUG_OUT.println(hysteresis);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -295,7 +302,7 @@ void InjectLog(){
|
||||
#if defined(IS_ATTINY_FAMILY)
|
||||
mySerial.print("!"); // --- MINIMALIST NOTIFICATION (ATtiny) ---
|
||||
#else
|
||||
Serial.println(" INJECT ! ");
|
||||
DEBUG_OUT.println(" INJECT ! ");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user