Files
SergObsidian/PERSONAL PROJECTS/PSX code - modules load and exec.md
2025-04-18 22:40:53 +05:00

6.9 KiB
Raw Blame History

Вот реализация вашей задачи для PlayStation 1, состоящая из трёх частей:

  1. Основная программа (MAIN.C) - загружает и запускает дополнительные модули.
  2. Модуль 1 (MODULE1.C) - выводит сообщение и изменяет общую переменную.
  3. Модуль 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. Компиляция и запись на диск

  1. Скомпилируйте каждый модуль отдельно:

    ccpsx MAIN.C -o MAIN.EXE
    ccpsx MODULE1.C -o MODULE1.BIN
    ccpsx MODULE2.C -o MODULE2.BIN
    
  2. Запишите на диск, указав точные сектора:

    MAIN.EXE    -> Сектор 0
    MODULE1.BIN -> Сектор 1000 (как указано в MAIN.C)
    MODULE2.BIN -> Сектор 1100
    
  3. В SYSTEM.CNF укажите:

    BOOT = cdrom:\MAIN.EXE;1
    

7. Как это работает

  1. Основная программа:

    • Загружает модули в 0x80030000 через CdRead().
    • Проверяет магическое число в заголовке.
    • Вызывает entry_point, передавая указатель на общие данные.
  2. Модули:

    • Компилируются с заголовком в начале.
    • Имеют доступ к SharedData через переданный указатель.
    • Разрешены только операции с данными (нельзя использовать PsyQ напрямую).
  3. Общие переменные:

    • Расположены в основной программе.
    • Модули получают к ним доступ через указатель.

8. Ограничения безопасности

  • Все модули должны доверять друг другу.
  • Нет защиты от переполнения буфера (адрес загрузки фиксирован).
  • Модули не должны переопределять системные вызовы.

9. Оптимизации

  • Используйте DMA для ускорения загрузки.
  • Добавьте CRC-проверку модулей в заголовке.
  • Для сложных модулей применяйте релокацию (но это сложнее).

Этот подход используется в таких играх, как Metal Gear Solid (для загрузки скриптовых модулей).