369 lines
14 KiB
Markdown
369 lines
14 KiB
Markdown
Вот доработанная версия с **общими переменными между основной программой и модулями**. Мы создадим единую структуру `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 <libetc.h>
|
||
#include <libcd.h>
|
||
#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 <libmdec.h>
|
||
#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 <libgpu.h>
|
||
#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 <libgpu.h> // Библиотека внутри модуля
|
||
|
||
void module_entry(GameState *state) {
|
||
// Использует свою копию LoadImage
|
||
RECT rect = {0, 0, 256, 256};
|
||
LoadImage(&rect, (uint32_t*)texture); // Функция из libgpu
|
||
}
|
||
```
|
||
|
||
- В основной программе (`MAIN.C`):
|
||
```c
|
||
// Нет #include <libgpu.h>!
|
||
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 <libcd.h> // CdRead(), CdInit()
|
||
#include <libetc.h> // FntPrint(), VSync()
|
||
#include <libgte.h> // Базовая математика (если используется)
|
||
```
|
||
|
||
#### Для **MODULE_GPU.C**:
|
||
```c
|
||
#include <libgpu.h> // LoadImage(), DrawPrim()
|
||
#include <libetc.h> // FntPrint() (если нужно)
|
||
```
|
||
|
||
---
|
||
|
||
### **6. Итог**
|
||
- **Основная программа** использует **минимальный набор PsyQ** (`libcd`, `libetc`).
|
||
- **Модули** включают **все необходимые им библиотеки** (`libgpu`, `libmdec` и т.д.).
|
||
- **Общение** между ними происходит через **общую структуру** `GameState`.
|
||
|
||
Спасибо за замечание! Теперь архитектура стала ещё понятнее 😊. |