vault backup: 2025-04-18 22:50:54
This commit is contained in:
@@ -1,206 +1,193 @@
|
|||||||
Вот реализация вашей задачи для **PlayStation 1**, состоящая из трёх частей:
|
Вот доработанная версия с **общими переменными между основной программой и модулями**. Мы создадим единую структуру `GameState`, которая будет передаваться в каждый модуль.
|
||||||
1. **Основная программа** (MAIN.C) - загружает и запускает дополнительные модули.
|
|
||||||
2. **Модуль 1** (MODULE1.C) - выводит сообщение и изменяет общую переменную.
|
|
||||||
3. **Модуль 2** (MODULE2.C) - читает ту же переменную и выводит изменённое значение.
|
|
||||||
|
|
||||||
Все модули загружаются в **один адрес RAM (0x80030000)** и используют общие переменные через специальный заголовок.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### **1. Структура проекта**
|
### **1. Общий заголовок (SHARED.H)**
|
||||||
```
|
|
||||||
project/
|
|
||||||
├── MAIN.C (Основная программа)
|
|
||||||
├── MODULE1.C (Модуль 1)
|
|
||||||
├── MODULE2.C (Модуль 2)
|
|
||||||
├── SHARED.H (Общие определения)
|
|
||||||
└── SYSTEM.CNF
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### **2. Общий заголовок (SHARED.H)**
|
|
||||||
```c
|
```c
|
||||||
// Структура для общих переменных
|
// Структура для общих переменных игры
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int counter;
|
int lives; // Количество жизней
|
||||||
char message[32];
|
int level; // Текущий уровень
|
||||||
} SharedData;
|
bool is_paused; // Состояние паузы
|
||||||
|
uint32_t score; // Счёт игрока
|
||||||
|
} GameState;
|
||||||
|
|
||||||
// Заголовок модуля (должен быть одинаковым во всех модулях)
|
// Заголовок модуля
|
||||||
typedef void (*ModuleFunc)(SharedData*);
|
typedef void (*ModuleEntry)(GameState*); // Тип функции-входа
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t magic; // Магическое число 0x55AA1234
|
uint32_t magic; // 0x55AA1234
|
||||||
ModuleFunc entry_point; // Точка входа
|
ModuleEntry entry; // Функция входа
|
||||||
uint32_t size; // Размер модуля
|
uint32_t size; // Размер модуля
|
||||||
} ModuleHeader;
|
} ModuleHeader;
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### **3. Основная программа (MAIN.C)**
|
### **2. Основная программа (MAIN.C)**
|
||||||
```c
|
```c
|
||||||
#include <sys/types.h>
|
|
||||||
#include <libetc.h>
|
#include <libetc.h>
|
||||||
#include <libgte.h>
|
|
||||||
#include <libcd.h>
|
#include <libcd.h>
|
||||||
#include "SHARED.H"
|
#include "SHARED.H"
|
||||||
|
|
||||||
#define MODULE_LOAD_ADDR 0x80030000 // Фиксированный адрес загрузки
|
#define MODULE_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; // Общие данные
|
GameState game_state = {
|
||||||
|
.lives = 3,
|
||||||
|
.level = 1,
|
||||||
|
.is_paused = false,
|
||||||
|
.score = 0
|
||||||
|
};
|
||||||
|
|
||||||
void load_and_run_module(int sector, int size) {
|
void load_and_run_module(int sector, int size) {
|
||||||
// 1. Загрузка модуля в фиксированный адрес
|
CdRead(sector, (uint32_t*)MODULE_ADDR, size);
|
||||||
CdRead(sector, (uint32_t*)MODULE_LOAD_ADDR, size);
|
|
||||||
CdReadSync(0);
|
CdReadSync(0);
|
||||||
|
|
||||||
// 2. Проверка заголовка
|
ModuleHeader *header = (ModuleHeader*)MODULE_ADDR;
|
||||||
ModuleHeader *header = (ModuleHeader*)MODULE_LOAD_ADDR;
|
if (header->magic == 0x55AA1234) {
|
||||||
if (header->magic != 0x55AA1234) {
|
header->entry(&game_state); // Передаём указатель на game_state
|
||||||
FntPrint("Invalid module!\n");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Вызов точки входа модуля
|
|
||||||
header->entry_point(&shared_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
ResetGraph(0);
|
ResetGraph(0);
|
||||||
InitPad(NULL, 0, NULL, 0);
|
|
||||||
CdInit();
|
CdInit();
|
||||||
SetVideoMode(MODE_NTSC);
|
|
||||||
FntLoad(960, 256);
|
FntLoad(960, 256);
|
||||||
FntOpen(16, 16, 256, 224, 0, 512);
|
FntOpen(16, 16, 256, 224, 0, 512);
|
||||||
|
|
||||||
// Инициализация общих данных
|
|
||||||
shared_data.counter = 0;
|
|
||||||
sprintf(shared_data.message, "Initial message");
|
|
||||||
|
|
||||||
// Загрузка и выполнение модулей по очереди
|
|
||||||
while (1) {
|
while (1) {
|
||||||
FntPrint("Main program running...\n");
|
// Обновляем общие переменные
|
||||||
|
game_state.score += 10;
|
||||||
// Загрузить и выполнить MODULE1
|
FntPrint("Main: Lives=%d, Score=%d\n", game_state.lives, game_state.score);
|
||||||
FntPrint("Loading MODULE1...\n");
|
|
||||||
load_and_run_module(MODULE1_SECTOR, MODULE1_SIZE);
|
|
||||||
VSync(0);
|
|
||||||
|
|
||||||
// Загрузить и выполнить MODULE2
|
// Загружаем и выполняем модули
|
||||||
FntPrint("Loading MODULE2...\n");
|
load_and_run_module(MODULE_MDEC_SECTOR, MODULE_MDEC_SIZE);
|
||||||
load_and_run_module(MODULE2_SECTOR, MODULE2_SIZE);
|
load_and_run_module(MODULE_GPU_SECTOR, MODULE_GPU_SIZE);
|
||||||
VSync(0);
|
|
||||||
|
|
||||||
FntFlush(-1);
|
|
||||||
VSync(0);
|
VSync(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### **4. Модуль 1 (MODULE1.C)**
|
### **3. Модуль для MDEC (MODULE_MDEC.C)**
|
||||||
```c
|
```c
|
||||||
|
#include <libmdec.h>
|
||||||
#include "SHARED.H"
|
#include "SHARED.H"
|
||||||
|
|
||||||
void module_entry(SharedData *shared) {
|
void module_entry(GameState *state) {
|
||||||
// Изменяем общие данные
|
// Читаем и изменяем общие переменные
|
||||||
shared->counter++;
|
if (state->lives > 0) {
|
||||||
sprintf(shared->message, "MODULE1 was here!");
|
state->lives--; // Уменьшаем жизни
|
||||||
|
FntPrint("MDEC: Lives left=%d\n", state->lives);
|
||||||
|
}
|
||||||
|
|
||||||
// Выводим сообщение (в реальности используйте FntPrint из основной программы)
|
// Декодируем видео только если игра не на паузе
|
||||||
// Здесь просто пример доступа к данным
|
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"))) = {
|
ModuleHeader __header__ __attribute__((section(".header"))) = {
|
||||||
.magic = 0x55AA1234,
|
.magic = 0x55AA1234,
|
||||||
.entry_point = module_entry,
|
.entry = 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
|
.size = 0
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### **6. Компиляция и запись на диск**
|
### **4. Модуль для GPU (MODULE_GPU.C)**
|
||||||
1. Скомпилируйте каждый модуль **отдельно**:
|
```c
|
||||||
```bash
|
#include <libgpu.h>
|
||||||
ccpsx MAIN.C -o MAIN.EXE
|
#include "SHARED.H"
|
||||||
ccpsx MODULE1.C -o MODULE1.BIN
|
|
||||||
ccpsx MODULE2.C -o MODULE2.BIN
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Запишите на диск, указав **точные сектора**:
|
void module_entry(GameState *state) {
|
||||||
```
|
// Проверяем глобальный статус
|
||||||
MAIN.EXE -> Сектор 0
|
if (state->is_paused) {
|
||||||
MODULE1.BIN -> Сектор 1000 (как указано в MAIN.C)
|
FntPrint("GPU: Game is paused!\n");
|
||||||
MODULE2.BIN -> Сектор 1100
|
return;
|
||||||
```
|
}
|
||||||
|
|
||||||
3. В `SYSTEM.CNF` укажите:
|
// Рисуем HUD с общими переменными
|
||||||
```
|
char hud_text[32];
|
||||||
BOOT = cdrom:\MAIN.EXE;1
|
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
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### **7. Как это работает**
|
### **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. **Основная программа**:
|
1. **Основная программа**:
|
||||||
- Загружает модули в `0x80030000` через `CdRead()`.
|
- Создаёт глобальную структуру `GameState`.
|
||||||
- Проверяет магическое число в заголовке.
|
- Передаёт **указатель** на неё в модули через `header->entry(&game_state)`.
|
||||||
- Вызывает `entry_point`, передавая указатель на общие данные.
|
|
||||||
|
|
||||||
2. **Модули**:
|
2. **Модули**:
|
||||||
- Компилируются с **заголовком** в начале.
|
- Читают и изменяют `GameState` через полученный указатель.
|
||||||
- Имеют доступ к `SharedData` через переданный указатель.
|
- Могут проверять:
|
||||||
- Разрешены **только операции с данными** (нельзя использовать PsyQ напрямую).
|
```c
|
||||||
|
if (state->is_paused) { ... }
|
||||||
|
```
|
||||||
|
- Могут изменять:
|
||||||
|
```c
|
||||||
|
state->lives--;
|
||||||
|
```
|
||||||
|
|
||||||
3. **Общие переменные**:
|
3. **Синхронизация**:
|
||||||
- Расположены в основной программе.
|
- Все изменения мгновенно видны в основном модуле и других модулях.
|
||||||
- Модули получают к ним доступ через указатель.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### **8. Ограничения безопасности**
|
### **7. Пример работы**
|
||||||
- Все модули **должны доверять** друг другу.
|
1. Основная программа:
|
||||||
- Нет защиты от **переполнения буфера** (адрес загрузки фиксирован).
|
- Устанавливает `lives = 3`, `score = 0`.
|
||||||
- Модули не должны **переопределять системные вызовы**.
|
2. Модуль MDEC:
|
||||||
|
- Уменьшает `lives` до 2, если игрок ошибся.
|
||||||
|
3. Модуль GPU:
|
||||||
|
- Отображает текущие `lives` и `score` на экране.
|
||||||
|
4. Основная программа:
|
||||||
|
- Видит изменения и может реагировать (например, завершить игру при `lives == 0`).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### **9. Оптимизации**
|
### **8. Важные нюансы**
|
||||||
- Используйте **DMA** для ускорения загрузки.
|
- **Безопасность**: Модули могут **перезаписать любые данные** в `GameState`. Добавьте проверки:
|
||||||
- Добавьте **CRC-проверку** модулей в заголовке.
|
```c
|
||||||
- Для сложных модулей применяйте **релокацию** (но это сложнее).
|
if (state->lives < 0) state->lives = 0; // Защита от отрицательных значений
|
||||||
|
```
|
||||||
|
- **Производительность**: Частый доступ к общим переменным может замедлить работу. Оптимизируйте:
|
||||||
|
```c
|
||||||
|
int local_lives = state->lives; // Кэширование в локальную переменную
|
||||||
|
```
|
||||||
|
- **Конфликты**: Если модули работают с одними данными, добавьте **флаги блокировки**.
|
||||||
|
|
||||||
Этот подход используется в таких играх, как **Metal Gear Solid** (для загрузки скриптовых модулей).
|
---
|
||||||
|
|
||||||
________________
|
|
||||||
|
|
||||||
|
Теперь модули могут **полноценно взаимодействовать** с основной программой, проверяя и изменяя глобальное состояние игры! 🎮
|
||||||
Reference in New Issue
Block a user