410 lines
21 KiB
NASM
410 lines
21 KiB
NASM
;
|
||
; Persona 2 Eternal Punishment (PSX) JAP / Custom Characters/Data Patch
|
||
; Author: Sergey Shemet 06/10/2022
|
||
|
||
.psx
|
||
|
||
// Определения функций
|
||
.definelabel SetDrawTPage, 0x800578fc
|
||
.definelabel storeColor, 0x8001c0b4
|
||
.definelabel initCopyCharChain, 0x8001b110
|
||
.definelabel MakeShadowSmallChar, 0x8001b2a8
|
||
.definelabel PrintBigDMAText, 0x8001a3a8
|
||
|
||
.definelabel MyAddr, 0x8009
|
||
|
||
.open "SLPS_028.25", 0x8000F800
|
||
|
||
.org 0x80090000
|
||
|
||
ExternalPrint:
|
||
// Текстовая тестовая процедура
|
||
lhu v0, 0x0(a0) // Читаем 2 байта текста
|
||
nop
|
||
srl v1, v0, 0xD // Проверяем 13-й бит (система 1 байт)
|
||
bne v1, zero, MyPrintLineRoutine // Если установлен, переходим к нашей процедуре
|
||
clear v1
|
||
j PrintBigDMAText // Иначе используем стандартную процедуру
|
||
nop
|
||
|
||
MyPrintLineRoutine:
|
||
addiu sp, sp, -0x50 // Выделяем место в стеке
|
||
sw s4, 0x38(sp) // Сохраняем регистры
|
||
move s4, a0 // Адрес чтения текста -> s4
|
||
sw s0, 0x28(sp)
|
||
move s0, a1
|
||
sw s1, 0x2c(sp)
|
||
move s1, a2
|
||
sw s3, 0x34(sp)
|
||
|
||
// Обработка новой команды
|
||
andi v0, v0, 0xff // Берем младший байт
|
||
addiu s4, s4, 0x2 // Сдвигаем указатель текста на +2 байта
|
||
|
||
lui s3, MyAddr
|
||
sw v0, -0x10(s3) // Сохраняем счетчик байтов @ 8008fff0
|
||
|
||
// Инициализация scratchpad
|
||
lui s3, 0x1f80
|
||
ori s3, s3, 0x3d0 // s3 = 1f8003d0 - команда копирования спрайта
|
||
lui t0, 0xff
|
||
ori t0, t0, 0xffff // t0 = 00FFFFFF
|
||
|
||
andi a0, a3, 0xff // Режим текста (цвет и тень)
|
||
lui a3, 0x8008 // a3 = 80080000
|
||
lui a2, 0xff00 // a2 = FF000000
|
||
|
||
addiu t1, a3, -0x31a8 // t1 = 8007b6d0 - новая свободная цепочка
|
||
lw v1, -0x31a8(a3) // v1 = загружаем свободную цепочку
|
||
lbu a1, 0x60(sp) // Флаг тени из стека -> a1
|
||
lui v0, 0x8000 // v0 = 80000000
|
||
sw ra, 0x48(sp) // Сохраняем регистры
|
||
sw s7, 0x44(sp)
|
||
sw s6, 0x40(sp)
|
||
sw s5, 0x3c(sp)
|
||
sw s2, 0x30(sp)
|
||
|
||
// Инициализация цепочек
|
||
and a2, v1, a2 // a2 = v1 & ff000000
|
||
andi a1, a1, 0xff // Очищаем режим текста
|
||
and v1, v1, t0 // v1 & 00ffffff
|
||
or s5, v1, v0 // s5 - основной адрес свободной цепочки
|
||
lw v0, 0x4(t1) // Загружаем счетчик DMA
|
||
lw v1, 0x0(s5) // v1 = следующая свободная цепочка
|
||
addiu v0, v0, -0x1 // Уменьшаем счетчик DMA
|
||
and v1, v1, t0 // v1 & 00ffffff
|
||
or a2, a2, v1 // a2 | v1 = следующая свободная цепочка
|
||
sw v0, 0x4(t1) // Сохраняем счетчик DMA
|
||
sw a2, -0x31a8(a3) // Сохраняем следующую свободную цепочку
|
||
jal storeColor
|
||
move a0, s5 // Текущий адрес цепочки
|
||
sll a1, s0, 0x10 // a1 = X << 16
|
||
sra a1, a1, 0x10 // a1 = X координата
|
||
sll a2, s1, 0x10 // a2 = Y << 16
|
||
jal initCopyCharChain // Инициализация цепочек символов
|
||
sra a2, a2, 0x10 // a2 = Y координата
|
||
|
||
// Инициализация ScratchPad (CPU to VRAM)
|
||
lui a2, 0x1f80
|
||
ori a2, a2, 0x348 // a2 = первый символ начала
|
||
|
||
// 4-битная таблица паттернов
|
||
.include "EP_4bitPattern.asm"
|
||
|
||
li v0, 0x2
|
||
sh v0, 0x1c(sp)
|
||
li v0, 0xc
|
||
li a0, 0x10 // Команды в цепочке формирования символа?
|
||
lui v1, 0xa000 // a0 = a0000000
|
||
sh v0, 0x1e(sp) // Сохраняем 000C0002 в стек+1c
|
||
|
||
// Инициализация символа
|
||
lui v0, 0x100 // Сброс кэша 01000000
|
||
sb a0, 0x3(a2) // Сохраняем количество команд
|
||
sw v1, 0x4(a2) // Сохраняем a0000000
|
||
|
||
lui at, 0x1f80 // Начало scratchpad
|
||
sw v0, 0x388(at) // Сохраняем в конец первой цепочки символов
|
||
|
||
// Формирование команды DMA копирования спрайта
|
||
li v0, 0x4
|
||
sb v0, 0x3(s3) // Длина цепочки = 4
|
||
li v0, 0x64
|
||
sb v0, 0x7(s3) // Команда копирования спрайта
|
||
|
||
lhu a0, 0x98(gp) // Загружаем X координату
|
||
lhu v1, 0x9a(gp) // Загружаем Y координату
|
||
|
||
li v0, 0x80
|
||
sb v0, 0x4(s3)
|
||
sb v0, 0x5(s3)
|
||
sb v0, 0x6(s3) // Формируем 80808064
|
||
|
||
setY1:
|
||
lw v0, 0x1c(sp) // v0 = 000c0002 из стека
|
||
lh a3, 0x98(gp) // Для дальнейших расчетов X спрайта
|
||
sh a0, 0x18(sp)
|
||
sh v1, 0x1a(sp) // Сохраняем координаты в стек
|
||
lw a0, 0x18(sp) // Загружаем координаты из стека
|
||
move t0, a3
|
||
sw a0, 0x8(a2) // Загружаем координаты в DMA
|
||
sw v0, 0xc(a2) // Сохраняем в первый символ
|
||
|
||
bgez a3, LAB_800194fc
|
||
|
||
SetX1:
|
||
sh s1, 0x20(sp) // Сохраняем X половину в sp+20
|
||
addiu t0, a3, 0x3f
|
||
|
||
LAB_800194fc:
|
||
// Декодирование координат для исходного спрайта
|
||
sra v0, t0, 0x6 // v0 = t0 >> 6
|
||
sll v0, v0, 0x6 // v0 << 6
|
||
subu v0, a3, v0 // v0 = a3 - v0
|
||
lh v1, 0x9a(gp) // Загружаем Y половину из памяти
|
||
sll v0, v0, 0x2 // v0 << 2
|
||
sb v0, 0xc(s3) // Сохраняем X байт спрайта символа
|
||
bgez v1, LAB_80019520 // Если Y >= 0 - ветвление
|
||
move v0, v1
|
||
addiu v0, v1, 0xff // Иначе v0 = ff - v1
|
||
|
||
LAB_80019520:
|
||
sra v0, v0, 0x8
|
||
sll v0, v0, 0x8
|
||
subu v0, v1, v0
|
||
sb v0, 0xd(s3) // Сохраняем Y байт спрайта символа
|
||
lhu v1, 0xa0(gp)
|
||
|
||
li v0, 0x8 // Ширина спрайта = 8
|
||
sh v0, 0x10(s3) // Сохраняем ширину
|
||
sh v1, 0xe(s3) // Сохраняем значение
|
||
|
||
sh s0, 0x8(s3) // Сохраняем X
|
||
sh s1, 0xa(s3) // Сохраняем Y
|
||
|
||
li v0, 0xc // Высота спрайта = 12
|
||
sh v0, 0x12(s3) // Сохраняем высоту
|
||
|
||
// Начало чтения текста
|
||
lbu a0, 0x0(s4) // Основное чтение байта команды
|
||
nop
|
||
addiu s4, s4, 0x1 // Сдвиг адреса чтения на +1 байт
|
||
|
||
SpaceCheckBranch:
|
||
jal makeCharPixelsCustom // Создание маленького символа в scratch
|
||
clear s2
|
||
|
||
lui t2, 0xff00 // t2 = ff000000
|
||
move t6, s5 // Параметры
|
||
addiu t5, sp, 0x10 // t5 = sp + 10
|
||
lui t3, 0x8008
|
||
addiu t4, t3, -0x31a8
|
||
move t1, s5 // Параметры снова
|
||
|
||
SetY:
|
||
SetX:
|
||
PageLoop:
|
||
// Создание цепочки копирования спрайта
|
||
lw v0, -0x3198(s6) // v0 = *адрес свободной цепочки
|
||
nop
|
||
and a0, v0, t2 // a0 = v0 & ff00..
|
||
and v0, v0, s0 // v0 & 00FFFFFF
|
||
or a1, v0, s1 // a1 = v0 | 80000000
|
||
lw v0, 0x4(s7) // Счетчик команд
|
||
lw v1, 0x0(a1) // Новый адрес свободной цепочки
|
||
addiu v0, v0, -0x1 // Уменьшаем счетчик
|
||
and v1, v1, s0 // v1 & 00FFFFFF
|
||
or a0, a0, v1 // a0 & v1
|
||
sw v0, 0x4(s7) // Сохраняем счетчик DMA
|
||
sw a0, -0x3198(s6) // Сохраняем новую свободную цепочку
|
||
|
||
CopyCmdFromScratch:
|
||
// Копирование команды из scratch
|
||
lw t9, 0x0(s3) // Количество команд DMA
|
||
lw t7, 0x4(s3) // Команда копирования спрайта
|
||
lw t8, 0x8(s3) // Координаты экрана XY
|
||
lw t5, 0xc(s3) // CLUT и координаты текстуры
|
||
sw t9, 0x0(a1)
|
||
sw t7, 0x4(a1)
|
||
sw t8, 0x8(a1)
|
||
sw t5, 0xc(a1)
|
||
lw t9, 0x10(s3) // Размер спрайта после копирования
|
||
nop
|
||
sw t9, 0x10(a1) // Сохраняем последнюю команду
|
||
|
||
lw v1, 0x18(t1) // Следующий параметр
|
||
lw v0, 0x0(a1) // Текущий новый адрес цепочки
|
||
lw v1, 0x0(v1) // Загружаем значение
|
||
and v0, v0, t2 // v0 & ff000000
|
||
and v1, v1, s0 // v1 & 00FFFFFF
|
||
or v0, v0, v1 // Команды с FFFFFF
|
||
sw v0, 0x0(a1) // Обновляем текущий адрес цепочки
|
||
lw a0, 0x18(t1) // Первый адрес цепочки
|
||
nop
|
||
lw v0, 0x0(a0) // Первая ссылка цепочки на следующую
|
||
and v1, a1, s0 // v1 = a1 & 00FFFFFF
|
||
and v0, v0, t2 // v0 & FF000000
|
||
or v0, v0, v1 // Последний адрес цепочки
|
||
sw v0, 0x0(a0) // Первая цепочка следующего адреса
|
||
sw a1, 0x18(t1) // Текущий адрес цепочки
|
||
lhu v0, 0x4(t6) // Счетчик символов
|
||
nop
|
||
bne v0, zero, NotFirstChar // Если счетчик символов ≠ 0, переходим
|
||
nop
|
||
|
||
sw a1, 0x28(t1) // Устанавливаем первую цепочку символов
|
||
sw t8, 0xc(t6) // Сохраняем координаты в параметры
|
||
// sh s2, 0xe(t4) // Сохраняем половину Y
|
||
|
||
NotFirstChar:
|
||
lui a1, 0x1f80 // Адрес символа здесь
|
||
lw v0, -0x31a8(t3) // Следующая свободная цепочка
|
||
ori a1, a1, 0x348 // a1 | 1f800348 = Scratch данные символа
|
||
and a0, v0, t2 // a0 = v0 & FF000000
|
||
and v0, v0, s0 // v0 & 00FFFFFF
|
||
or a3, v0, s1 // a3 = следующая цепочка & 80...
|
||
move a2, a3 // a2 = a3 (следующая цепочка)
|
||
lw v0, 0x4(t4) // Счетчик команд
|
||
lw v1, 0x0(a3) // Новый адрес свободной цепочки
|
||
addiu v0, v0, -0x1 // Уменьшаем счетчик
|
||
and v1, v1, s0 // v1 & 80... количество команд
|
||
or a0, a0, v1 // a0 & v1
|
||
sw v0, 0x4(t4) // Сохраняем счетчик DMA
|
||
andi v0, a3, 0x3 // Проверяем адрес назначения MOD 4
|
||
sw a0, -0x31a8(t3) // Сохраняем новую свободную цепочку
|
||
|
||
beq v0, zero, Copy16Bytes // Если MOD 4 = 0, ветвление
|
||
addiu v0, a1, 0x40
|
||
|
||
CopyBy2Bytes:
|
||
// Копируем по 2 байта если MOD 4 ≠ 0
|
||
lwl t9, 0x3(a1)
|
||
lwr t9, 0x0(a1)
|
||
lwl t7, 0x7(a1)
|
||
lwr t7, 0x4(a1)
|
||
lwl t8, 0xb(a1)
|
||
lwr t8, 0x8(a1)
|
||
lwl t5, 0xf(a1)
|
||
lwr t5, 0xc(a1)
|
||
swl t9, 0x3(a2)
|
||
swr t9, 0x0(a2)
|
||
swl t7, 0x7(a2)
|
||
swr t7, 0x4(a2)
|
||
swl t8, 0xb(a2)
|
||
swr t8, 0x8(a2)
|
||
swl t5, 0xf(a2)
|
||
swr t5, 0xc(a2)
|
||
addiu a1, a1, 0x10
|
||
bne a1, v0, CopyBy2Bytes
|
||
addiu a2, a2, 0x10
|
||
j last4BytesCopy
|
||
nop
|
||
|
||
Copy16Bytes:
|
||
// Копируем по 16 байт
|
||
lw t9, 0x0(a1)
|
||
lw t7, 0x4(a1)
|
||
lw t8, 0x8(a1)
|
||
lw t5, 0xc(a1)
|
||
sw t9, 0x0(a2)
|
||
sw t7, 0x4(a2)
|
||
sw t8, 0x8(a2)
|
||
sw t5, 0xc(a2)
|
||
addiu a1, a1, 0x10
|
||
bne a1, v0, Copy16Bytes
|
||
addiu a2, a2, 0x10
|
||
|
||
last4BytesCopy:
|
||
// Копируем последние 4 байта
|
||
lwl t9, 0x3(a1)
|
||
lwr t9, 0x0(a1)
|
||
nop
|
||
swl t9, 0x3(a2)
|
||
swr t9, 0x0(a2)
|
||
|
||
// Связывание DMA символа + копирование спрайта
|
||
lw v1, 0x18(t1) // Текущая цепочка DMA копирования спрайта
|
||
lw v0, 0x0(a3) // Текущие команды цепочки символов
|
||
lw v1, 0x0(v1) // Количество команд + следующая ссылка
|
||
and v0, v0, t2 // v0 & FF.....
|
||
and v1, v1, s0 // v1 & 00FFFFFF
|
||
or v0, v0, v1 // Количество команд & FF..
|
||
sw v0, 0x0(a3) // Сохраняем текущую команду цепочки
|
||
lw a0, 0x18(t1) // Адрес заголовка текущей команды копирования спрайта
|
||
nop
|
||
lw v0, 0x0(a0) // 04FFFFFF
|
||
and v1, a3, s0 // v1 = копирование текущей цепочки символов
|
||
and v0, v0, t2 // v0 & FF000000 (количество команд)
|
||
or v0, v0, v1 // Полная команда со ссылкой
|
||
sw v0, 0x0(a0) // Сохраняем с ссылкой на цепочку символов
|
||
sw a3, 0x18(t1) // Последняя цепочка символов
|
||
|
||
addiu t1, t1, 0x4 // Сдвигаем параметры для следующей страницы
|
||
addiu v0, t6, 0x8 // Сдвигаем первый параметр цепочки для страницы
|
||
sltu v0, t1, v0 // Проверяем прохождение первой страницы
|
||
bne v0, zero, PageLoop // Если не прошли, продолжаем цикл страниц
|
||
nop
|
||
|
||
// Сдвиг X координаты (+6)
|
||
lhu v0, 0xa8(gp) // Загружаем начальный X
|
||
lhu v1, 0x8(s3) // Загружаем последний X в scratch
|
||
addiu v0, v0, 0x6 // v0 = начальный X + 6
|
||
addu v1, v1, v0 // Последний X = последний X + (начальный X + 6)
|
||
sh v1, 0x8(s3) // Обновляем последний X в DMA scratch
|
||
|
||
SpaceJump:
|
||
// Увеличиваем счетчик символов + проверка
|
||
lhu v0, 0x4(s5) // Текущее количество символов DMA
|
||
lui t9, MyAddr
|
||
addiu v0, v0, 0x1 // Увеличиваем на 1
|
||
sh v0, 0x4(s5) // Сохраняем счетчик в цепочку
|
||
|
||
// Проверка переполнения DMA
|
||
lw v0, 0x4(s7) // Счетчик команд DMA
|
||
move v1, 5 // Минимальный счетчик команд
|
||
sltu v0, v0, v1 // v0 < v1?
|
||
bne v0, zero, TextEnd // Если переполнение DMA, завершаем текст
|
||
nop
|
||
|
||
lui t3, MyAddr
|
||
lhu v0, -0xE(t3) // Количество символов в строке
|
||
lhu v1, -0x10(t3) // Общее количество символов в строке
|
||
addiu v0, v0, 0x1 // Увеличиваем на 1
|
||
lbu a0, 0x0(s4) // Загружаем следующий символ
|
||
sh v0, -0xE(t3) // Сохраняем счетчик символов
|
||
addiu s4, s4, 0x1 // Сдвигаем адрес символа
|
||
|
||
bne v0, v1, NextChar // Если текущий символ ≠ общему количеству, продолжаем
|
||
nop
|
||
|
||
TextEnd:
|
||
clear s2
|
||
lui s3, 0x8008
|
||
addiu s4, s3, -0x3198 // s4 = 7b6d0
|
||
lui s1, 0xff
|
||
ori s1, s1, 0xffff // s1 = 00ffffff
|
||
move s0, s5 // s0 = текущие параметры строки
|
||
clear a1
|
||
|
||
PageTLoop:
|
||
// Завершающая обработка страниц
|
||
li a2, 0x1 // a2 = 1
|
||
addu s2, s2, a2 // Увеличиваем счетчик
|
||
lui v0, 0x8000 // v0 = 80000000
|
||
lui a0, 0xff00 // a0 = ff000000
|
||
lw v1, -0x3198(s3) // Следующая свободная цепочка
|
||
lw a3, 0x9c(gp) // Некоторые параметры (почти DMA)
|
||
and v1, v1, s1 // v1 & 00FFFFFF
|
||
or v1, v1, v0 // v1 | v0 = 80.....
|
||
sw v1, 0x30(s0) // Сохраняем последний свободный адрес в параметрах DMA
|
||
lw v0, -0x3198(s3) // Следующая свободная цепочка
|
||
lw v1, 0x0(v1) // Создаем еще одну цепочку
|
||
and v0, v0, a0 // v0 & ff000000
|
||
and v1, v1, s1 // v1 & 00FFFFFF
|
||
lw a0, 0x4(s4) // Счетчик DMA
|
||
or v0, v0, v1 // v0 | v1
|
||
sw v0, -0x3198(s3) // Новый адрес свободной цепочки
|
||
addiu a0, a0, -0x1 // Уменьшаем счетчик DMA
|
||
sw a0, 0x4(s4) // Сохраняем счетчик DMA
|
||
lw a0, 0x30(s0) // Начало цепочки DMA
|
||
jal SetDrawTPage // Устанавливаем страницу отрисовки
|
||
addiu s0, s0, 0x4 // Сдвигаем параметры DMA для следующей страницы
|
||
sltiu v0, s2, 0x2 // Проверяем счетчик < 2
|
||
bne v0, zero, PageTLoop // Если да, продолжаем цикл
|
||
clear a1 // a1 = 0
|
||
|
||
move v0, s5 // Возвращаем основные параметры DMA
|
||
|
||
// Восстанавливаем регистры из стека
|
||
lw ra, 0x48(sp)
|
||
lw s7, 0x44(sp)
|
||
lw s6, 0x40(sp)
|
||
lw s5, 0x3c(sp)
|
||
lw s4, 0x38(sp)
|
||
lw s3, 0x34(sp)
|
||
lw s2, 0x30(sp)
|
||
lw s1, 0x2c(sp)
|
||
lw s0, 0x28(sp)
|
||
|
||
jr ra // Возврат
|
||
addiu sp, sp, 0x50 // Восстанавливаем стек |