Files
SergObsidian/PERSONAL PROJECTS/PS1 DOCS/PSX code inline asm.md
2025-04-18 23:10:56 +05:00

4.8 KiB
Raw Permalink Blame History

Вот готовая программа для PlayStation 1, которая использует MIPS-ассемблерные вставки для сложения двух чисел через указатели. Результат возвращается в переменную основной программы.


1. Полный код (MAIN.C)

#include <stdio.h>
#include <libetc.h>

// Объявляем ассемблерную функцию
int add_numbers_asm(int *a, int *b, int *result);

int main() {
    int num1 = 5;
    int num2 = 7;
    int result = 0;

    // Вызываем ассемблерную функцию с указателями
    add_numbers_asm(&num1, &num2, &result);

    // Выводим результат
    FntPrint("Result: %d + %d = %d\n", num1, num2, result);
    FntFlush(-1);

    while (1) {
        VSync(0);
    }

    return 0;
}

// Реализация на MIPS-ассемблере
int add_numbers_asm(int *a, int *b, int *result) {
    int ret;
    asm volatile (
        "lw $t0, 0(%1)       \n" // Загружаем *a в $t0
        "lw $t1, 0(%2)       \n" // Загружаем *b в $t1
        "add $t2, $t0, $t1   \n" // Складываем: $t2 = $t0 + $t1
        "sw $t2, 0(%3)       \n" // Сохраняем результат по адресу *result
        "move %0, $t2        \n" // Возвращаем результат через регистр
        : "=r" (ret)             // Выходные операнды
        : "r" (a), "r" (b), "r" (result) // Входные операнды
        : "$t0", "$t1", "$t2"    // Разрушаемые регистры
    );
    return ret;
}

2. Как это работает?

  1. Передача указателей в ассемблер
    Функция add_numbers_asm принимает три указателя:

    • a — адрес первого числа,
    • b — адрес второго числа,
    • result — адрес для сохранения результата.
  2. Ассемблерная вставка (asm volatile):

    • lw — загружает значения из памяти по указателям.
    • add — складывает числа.
    • sw — сохраняет результат по адресу result.
    • move — возвращает значение через регистр.
  3. Доступ к результату
    Результат будет:

    • В переменной result (через указатель).
    • В возвращаемом значении функции (если нужно).

3. Компиляция и запуск

  1. Скомпилируйте через PsyQ SDK:

    ccpsx MAIN.C -o ADD_DEMO.EXE
    
  2. Запишите на диск и запустите на эмуляторе или консоли.
    На экране появится:

    Result: 5 + 7 = 12
    

4. Важные нюансы

  • Регистры MIPS:

    • $t0$t9 — временные регистры (можно использовать без сохранения).
    • $s0$s7 — должны сохраняться (если используются).
  • Разрушаемые регистры:
    Указаны в конце asm volatile (чтобы компилятор не использовал их для оптимизации).

  • Опасность:
    Если передать неверные указатели, PS1 уйдёт в краш (нет защиты памяти).


5. Пример с глобальными переменными

Если нужно работать с глобальными переменными напрямую:

int global_num1 = 10;
int global_num2 = 20;

void add_globals_asm() {
    asm volatile (
        "la $t0, global_num1 \n"  // Загружаем адрес global_num1
        "lw $t1, 0($t0)      \n"  // Читаем значение
        "la $t0, global_num2 \n"
        "lw $t2, 0($t0)      \n"
        "add $t1, $t1, $t2   \n"
        "sw $t1, 0($t0)      \n"  // Сохраняем сумму в global_num2
    );
}

6. Итог

  • Ассемблерные вставки дают полный контроль над железом PS1.
  • Указатели передаются через регистры ($a0$a3 в MIPS).
  • Результат можно получить через память или возвращаемое значение.

Этот подход используется в оптимизированных частях игр (например, в рендеринге или физике).