6.9 KiB
6.9 KiB
Вот реализация вашей задачи для PlayStation 1, состоящая из трёх частей:
- Основная программа (MAIN.C) - загружает и запускает дополнительные модули.
- Модуль 1 (MODULE1.C) - выводит сообщение и изменяет общую переменную.
- Модуль 2 (MODULE2.C) - читает ту же переменную и выводит изменённое значение.
Все модули загружаются в один адрес RAM (0x80030000) и используют общие переменные через специальный заголовок.
1. Структура проекта
project/
├── MAIN.C (Основная программа)
├── MODULE1.C (Модуль 1)
├── MODULE2.C (Модуль 2)
├── SHARED.H (Общие определения)
└── SYSTEM.CNF
2. Общий заголовок (SHARED.H)
// Структура для общих переменных
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)
#include <sys/types.h>
#include <libetc.h>
#include <libgte.h>
#include <libcd.h>
#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)
#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)
#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. Компиляция и запись на диск
-
Скомпилируйте каждый модуль отдельно:
ccpsx MAIN.C -o MAIN.EXE ccpsx MODULE1.C -o MODULE1.BIN ccpsx MODULE2.C -o MODULE2.BIN -
Запишите на диск, указав точные сектора:
MAIN.EXE -> Сектор 0 MODULE1.BIN -> Сектор 1000 (как указано в MAIN.C) MODULE2.BIN -> Сектор 1100 -
В
SYSTEM.CNFукажите:BOOT = cdrom:\MAIN.EXE;1
7. Как это работает
-
Основная программа:
- Загружает модули в
0x80030000черезCdRead(). - Проверяет магическое число в заголовке.
- Вызывает
entry_point, передавая указатель на общие данные.
- Загружает модули в
-
Модули:
- Компилируются с заголовком в начале.
- Имеют доступ к
SharedDataчерез переданный указатель. - Разрешены только операции с данными (нельзя использовать PsyQ напрямую).
-
Общие переменные:
- Расположены в основной программе.
- Модули получают к ним доступ через указатель.
8. Ограничения безопасности
- Все модули должны доверять друг другу.
- Нет защиты от переполнения буфера (адрес загрузки фиксирован).
- Модули не должны переопределять системные вызовы.
9. Оптимизации
- Используйте DMA для ускорения загрузки.
- Добавьте CRC-проверку модулей в заголовке.
- Для сложных модулей применяйте релокацию (но это сложнее).
Этот подход используется в таких играх, как Metal Gear Solid (для загрузки скриптовых модулей).