Вот доработанная версия с **общими переменными между основной программой и модулями**. Мы создадим единую структуру `GameState`, которая будет передаваться в каждый модуль. --- ### **1. Общий заголовок (SHARED.H)** ```c // Структура для общих переменных игры typedef struct { int lives; // Количество жизней int level; // Текущий уровень bool is_paused; // Состояние паузы uint32_t score; // Счёт игрока } GameState; // Заголовок модуля typedef void (*ModuleEntry)(GameState*); // Тип функции-входа typedef struct { uint32_t magic; // 0x55AA1234 ModuleEntry entry; // Функция входа uint32_t size; // Размер модуля } ModuleHeader; ``` --- ### **2. Основная программа (MAIN.C)** ```c #include #include #include "SHARED.H" #define MODULE_ADDR 0x80030000 // Адрес загрузки модулей GameState game_state = { .lives = 3, .level = 1, .is_paused = false, .score = 0 }; void load_and_run_module(int sector, int size) { CdRead(sector, (uint32_t*)MODULE_ADDR, size); CdReadSync(0); ModuleHeader *header = (ModuleHeader*)MODULE_ADDR; if (header->magic == 0x55AA1234) { header->entry(&game_state); // Передаём указатель на game_state } } int main() { ResetGraph(0); CdInit(); FntLoad(960, 256); FntOpen(16, 16, 256, 224, 0, 512); while (1) { // Обновляем общие переменные game_state.score += 10; FntPrint("Main: Lives=%d, Score=%d\n", game_state.lives, game_state.score); // Загружаем и выполняем модули load_and_run_module(MODULE_MDEC_SECTOR, MODULE_MDEC_SIZE); load_and_run_module(MODULE_GPU_SECTOR, MODULE_GPU_SIZE); VSync(0); } } ``` --- ### **3. Модуль для MDEC (MODULE_MDEC.C)** ```c #include #include "SHARED.H" void module_entry(GameState *state) { // Читаем и изменяем общие переменные if (state->lives > 0) { state->lives--; // Уменьшаем жизни FntPrint("MDEC: Lives left=%d\n", state->lives); } // Декодируем видео только если игра не на паузе if (!state->is_paused) { uint8_t data[1024]; uint16_t buffer[256*256]; MDEC_DecodeFrame(data, buffer, MDEC_MODE_RGB24); } } ModuleHeader __header__ __attribute__((section(".header"))) = { .magic = 0x55AA1234, .entry = module_entry, .size = 0 }; ``` --- ### **4. Модуль для GPU (MODULE_GPU.C)** ```c #include #include "SHARED.H" void module_entry(GameState *state) { // Проверяем глобальный статус if (state->is_paused) { FntPrint("GPU: Game is paused!\n"); return; } // Рисуем HUD с общими переменными char hud_text[32]; sprintf(hud_text, "LIVES: %d SCORE: %d", state->lives, state->score); FntPrint(hud_text); // Пример работы с GPU RECT rect = {10, 10, 100, 100}; uint16_t color = state->lives > 1 ? 0xFF00 : 0xFF0000; // Красный если 1 жизнь DrawPrim(&rect); } ModuleHeader __header__ __attribute__((section(".header"))) = { .magic = 0x55AA1234, .entry = module_entry, .size = 0 }; ``` --- ### **5. Сборка** ```bash # Компиляция модулей с их библиотеками ccpsx MODULE_MDEC.C -o MODULE_MDEC.BIN -lpsxmdec ccpsx MODULE_GPU.C -o MODULE_GPU.BIN -lpsxgpu # Основная программа (без лишних библиотек) ccpsx MAIN.C -o MAIN.EXE ``` --- ### **6. Механика общих переменных** 1. **Основная программа**: - Создаёт глобальную структуру `GameState`. - Передаёт **указатель** на неё в модули через `header->entry(&game_state)`. 2. **Модули**: - Читают и изменяют `GameState` через полученный указатель. - Могут проверять: ```c if (state->is_paused) { ... } ``` - Могут изменять: ```c state->lives--; ``` 3. **Синхронизация**: - Все изменения мгновенно видны в основном модуле и других модулях. --- ### **7. Пример работы** 1. Основная программа: - Устанавливает `lives = 3`, `score = 0`. 2. Модуль MDEC: - Уменьшает `lives` до 2, если игрок ошибся. 3. Модуль GPU: - Отображает текущие `lives` и `score` на экране. 4. Основная программа: - Видит изменения и может реагировать (например, завершить игру при `lives == 0`). --- ### **8. Важные нюансы** - **Безопасность**: Модули могут **перезаписать любые данные** в `GameState`. Добавьте проверки: ```c if (state->lives < 0) state->lives = 0; // Защита от отрицательных значений ``` - **Производительность**: Частый доступ к общим переменным может замедлить работу. Оптимизируйте: ```c int local_lives = state->lives; // Кэширование в локальную переменную ``` - **Конфликты**: Если модули работают с одними данными, добавьте **флаги блокировки**. --- Теперь модули могут **полноценно взаимодействовать** с основной программой, проверяя и изменяя глобальное состояние игры! 🎮 ------- Да, вы правильно поняли! В этой архитектуре **основная программа** и **модули** компилируются по-разному: --- ### **1. Основная программа (MAIN.EXE)** - **Не требует** явного указания библиотек PsyQ (если не использует их напрямую). - Её задача — только **загружать модули** и **управлять общим состоянием** (`GameState`). - Пример компиляции: ```bash ccpsx MAIN.C -o MAIN.EXE # Без -lpsxetc, -lpsxgpu и т.д. ``` --- ### **2. Дополнительные модули (MODULE_*.BIN)** - **Должны включать** нужные библиотеки PsyQ, так как: - Каждый модуль — **самодостаточный** исполняемый код. - PsyQ не загружается глобально для модулей (они работают в изолированном контексте). - Пример компиляции: ```bash # Модуль для GPU ccpsx MODULE_GPU.C -o MODULE_GPU.BIN -lpsxgpu -lpsxgte # Модуль для MDEC ccpsx MODULE_MDEC.C -o MODULE_MDEC.BIN -lpsxmdec ``` --- ### **3. Почему такая разница?** | Компонент | Библиотеки PsyQ | Причина | |--------------------|----------------|-------------------------------------------------------------------------| | **Основная программа** | Не нужны | Только загружает модули и передаёт `GameState`. Не использует GPU/MDEC. | | **Модули** | Обязательны | Каждый модуль — независимый код, требующий свои библиотеки. | --- ### **4. Как это работает в памяти?** 1. Основная программа: - Занимает **нижнюю часть RAM** (например, `0x80010000`). - Не знает о существовании PsyQ-функций. 2. Модули: - Загружаются в **фиксированный адрес** (`0x80030000`). - Содержат **свои копии библиотек** (например, `libgpu`). - Получают доступ к `GameState` через указатель. --- ### **5. Пример: вызов функций** - В модуле `MODULE_GPU.C`: ```c #include // Библиотека внутри модуля void module_entry(GameState *state) { // Использует свою копию LoadImage RECT rect = {0, 0, 256, 256}; LoadImage(&rect, (uint32_t*)texture); // Функция из libgpu } ``` - В основной программе (`MAIN.C`): ```c // Нет #include ! void load_module() { // Только загрузка бинарника CdRead(sector, (uint32_t*)0x80030000, size); } ``` --- ### **6. Ограничения** - **Дублирование кода**: Каждый модуль содержит свои библиотеки → увеличивает размер. - **Нет глобального состояния**: Если `libgpu` хранит внутренние переменные, они будут разными для каждого модуля. - **Только последовательное выполнение**: Модули не могут работать одновременно (перезаписывают один регион памяти). --- ### **7. Оптимизация** Если модули используют **одни и те же библиотеки**, можно: 1. Вынести общие библиотеки в **отдельный загружаемый модуль**. 2. Передавать **указатели на функции** (как в предыдущих примерах). --- ### **8. Итог** - **Основная программа** — минималистична, без зависимостей. - **Модули** — самодостаточные бинарники со своими библиотеками. - **Общение** через общую структуру `GameState`. Это архитектурно напоминает **плагины в современных движках**, где каждый плагин собирается со своими зависимостями. ------------------ Да, вы абсолютно правы! Моя ошибка — **основная программа действительно использует `libcd.h` (и другие библиотеки PsyQ для базовых операций)**, но **ключевое различие** в следующем: --- ### **1. Уточнение архитектуры** | Компонент | Библиотеки PsyQ | Почему? | |--------------------|------------------------------------|-------------------------------------------------------------------------| | **Основная программа** | `libcd.h`, `libetc.h`, `libgte.h` | Только для **загрузки модулей**, управления памятью и отладки (FntPrint). Не использует GPU/MDEC/SPU. | | **Модули** | Свои специфичные библиотеки (`libgpu`, `libmdec`) | Каждый модуль — самостоятельный код, требующий **все свои зависимости**. | --- ### **2. Исправленный пример компиляции** #### **Основная программа (MAIN.C)** ```bash # Нужны только базовые библиотеки для загрузки модулей ccpsx MAIN.C -o MAIN.EXE -lpsxcd -lpsxetc -lpsxgte ``` #### **Модули** ```bash # GPU-модуль (со своим libgpu) ccpsx MODULE_GPU.C -o MODULE_GPU.BIN -lpsxgpu -lpsxgte # MDEC-модуль (со своим libmdec) ccpsx MODULE_MDEC.C -o MODULE_MDEC.BIN -lpsxmdec ``` --- ### **3. Почему основная программа не включает `libgpu`/`libmdec`?** - Она **не вызывает** функции вроде `LoadImage()` или `MDEC_Decode()`. - Её задача — лишь: - Чтение секторов с диска (`libcd`). - Вывод текста (`libetc`). - Передача управления модулям. --- ### **4. Аналогия с операционной системой** - **Основная программа** — как **ядро ОС**, которое: - Умеет загружать драйверы (модули). - Не содержит код для работы с графикой/звуком. - **Модули** — как **драйверы**, которые: - Содержат весь специфичный код (например, для GPU). - Получают доступ к «ядру» через общие структуры (`GameState`). --- ### **5. Полный список зависимостей** #### Для **MAIN.C**: ```c #include // CdRead(), CdInit() #include // FntPrint(), VSync() #include // Базовая математика (если используется) ``` #### Для **MODULE_GPU.C**: ```c #include // LoadImage(), DrawPrim() #include // FntPrint() (если нужно) ``` --- ### **6. Итог** - **Основная программа** использует **минимальный набор PsyQ** (`libcd`, `libetc`). - **Модули** включают **все необходимые им библиотеки** (`libgpu`, `libmdec` и т.д.). - **Общение** между ними происходит через **общую структуру** `GameState`. Спасибо за замечание! Теперь архитектура стала ещё понятнее 😊.