Files
SergObsidian/PERSONAL PROJECTS/PS1 DOCS/PSX code - modules load and exec.md
2025-04-18 23:10:56 +05:00

369 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Вот доработанная версия с **общими переменными между основной программой и модулями**. Мы создадим единую структуру `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`.
Спасибо за замечание! Теперь архитектура стала ещё понятнее 😊.