Вот реализация вашей задачи для **PlayStation 1**, состоящая из трёх частей: 1. **Основная программа** (MAIN.C) - загружает и запускает дополнительные модули. 2. **Модуль 1** (MODULE1.C) - выводит сообщение и изменяет общую переменную. 3. **Модуль 2** (MODULE2.C) - читает ту же переменную и выводит изменённое значение. Все модули загружаются в **один адрес RAM (0x80030000)** и используют общие переменные через специальный заголовок. --- ### **1. Структура проекта** ``` project/ ├── MAIN.C (Основная программа) ├── MODULE1.C (Модуль 1) ├── MODULE2.C (Модуль 2) ├── SHARED.H (Общие определения) └── SYSTEM.CNF ``` --- ### **2. Общий заголовок (SHARED.H)** ```c // Структура для общих переменных typedef struct { int counter; char message[32]; } SharedData; // Заголовок модуля (должен быть одинаковым во всех модулях) typedef void (*ModuleFunc)(SharedData*); typedef struct { uint32_t magic; // Магическое число 0x55AA1234 ModuleFunc entry_point; // Точка входа uint32_t size; // Размер модуля } ModuleHeader; ``` --- ### **3. Основная программа (MAIN.C)** ```c #include #include #include #include #include "SHARED.H" #define MODULE_LOAD_ADDR 0x80030000 // Фиксированный адрес загрузки #define MODULE1_SECTOR 1000 // Сектор начала MODULE1.BIN #define MODULE1_SIZE 50 // Размер MODULE1.BIN в секторах #define MODULE2_SECTOR 1100 // Сектор начала MODULE2.BIN #define MODULE2_SIZE 50 // Размер MODULE2.BIN в секторах SharedData shared_data; // Общие данные void load_and_run_module(int sector, int size) { // 1. Загрузка модуля в фиксированный адрес CdRead(sector, (uint32_t*)MODULE_LOAD_ADDR, size); CdReadSync(0); // 2. Проверка заголовка ModuleHeader *header = (ModuleHeader*)MODULE_LOAD_ADDR; if (header->magic != 0x55AA1234) { FntPrint("Invalid module!\n"); return; } // 3. Вызов точки входа модуля header->entry_point(&shared_data); } int main() { ResetGraph(0); InitPad(NULL, 0, NULL, 0); CdInit(); SetVideoMode(MODE_NTSC); FntLoad(960, 256); FntOpen(16, 16, 256, 224, 0, 512); // Инициализация общих данных shared_data.counter = 0; sprintf(shared_data.message, "Initial message"); // Загрузка и выполнение модулей по очереди while (1) { FntPrint("Main program running...\n"); // Загрузить и выполнить MODULE1 FntPrint("Loading MODULE1...\n"); load_and_run_module(MODULE1_SECTOR, MODULE1_SIZE); VSync(0); // Загрузить и выполнить MODULE2 FntPrint("Loading MODULE2...\n"); load_and_run_module(MODULE2_SECTOR, MODULE2_SIZE); VSync(0); FntFlush(-1); VSync(0); } return 0; } ``` --- ### **4. Модуль 1 (MODULE1.C)** ```c #include "SHARED.H" void module_entry(SharedData *shared) { // Изменяем общие данные shared->counter++; sprintf(shared->message, "MODULE1 was here!"); // Выводим сообщение (в реальности используйте FntPrint из основной программы) // Здесь просто пример доступа к данным } // Важно: заголовок должен быть в конце файла! ModuleHeader __header__ __attribute__((section(".header"))) = { .magic = 0x55AA1234, .entry_point = module_entry, .size = 0 // Заполнит компоновщик }; ``` --- ### **5. Модуль 2 (MODULE2.C)** ```c #include "SHARED.H" void module_entry(SharedData *shared) { // Читаем изменённые данные shared->counter += 10; sprintf(shared->message + 8, " + MODULE2!"); // Выводим сообщение } ModuleHeader __header__ __attribute__((section(".header"))) = { .magic = 0x55AA1234, .entry_point = module_entry, .size = 0 }; ``` --- ### **6. Компиляция и запись на диск** 1. Скомпилируйте каждый модуль **отдельно**: ```bash ccpsx MAIN.C -o MAIN.EXE ccpsx MODULE1.C -o MODULE1.BIN ccpsx MODULE2.C -o MODULE2.BIN ``` 2. Запишите на диск, указав **точные сектора**: ``` MAIN.EXE -> Сектор 0 MODULE1.BIN -> Сектор 1000 (как указано в MAIN.C) MODULE2.BIN -> Сектор 1100 ``` 3. В `SYSTEM.CNF` укажите: ``` BOOT = cdrom:\MAIN.EXE;1 ``` --- ### **7. Как это работает** 1. **Основная программа**: - Загружает модули в `0x80030000` через `CdRead()`. - Проверяет магическое число в заголовке. - Вызывает `entry_point`, передавая указатель на общие данные. 2. **Модули**: - Компилируются с **заголовком** в начале. - Имеют доступ к `SharedData` через переданный указатель. - Разрешены **только операции с данными** (нельзя использовать PsyQ напрямую). 3. **Общие переменные**: - Расположены в основной программе. - Модули получают к ним доступ через указатель. --- ### **8. Ограничения безопасности** - Все модули **должны доверять** друг другу. - Нет защиты от **переполнения буфера** (адрес загрузки фиксирован). - Модули не должны **переопределять системные вызовы**. --- ### **9. Оптимизации** - Используйте **DMA** для ускорения загрузки. - Добавьте **CRC-проверку** модулей в заголовке. - Для сложных модулей применяйте **релокацию** (но это сложнее). Этот подход используется в таких играх, как **Metal Gear Solid** (для загрузки скриптовых модулей). ________________