Вот готовая программа для **PlayStation 1**, которая использует **MIPS-ассемблерные вставки** для сложения двух чисел через указатели. Результат возвращается в переменную основной программы. --- ### **1. Полный код (MAIN.C)** ```c #include #include // Объявляем ассемблерную функцию 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**: ```bash ccpsx MAIN.C -o ADD_DEMO.EXE ``` 2. Запишите на диск и запустите на эмуляторе или консоли. На экране появится: ``` Result: 5 + 7 = 12 ``` --- ### **4. Важные нюансы** - **Регистры MIPS**: - `$t0`–`$t9` — временные регистры (можно использовать без сохранения). - `$s0`–`$s7` — должны сохраняться (если используются). - **Разрушаемые регистры**: Указаны в конце `asm volatile` (чтобы компилятор не использовал их для оптимизации). - **Опасность**: Если передать **неверные указатели**, PS1 уйдёт в **краш** (нет защиты памяти). --- ### **5. Пример с глобальными переменными** Если нужно работать с глобальными переменными напрямую: ```c 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). - **Результат** можно получить через память или возвращаемое значение. Этот подход используется в оптимизированных частях игр (например, в рендеринге или физике).