Files
Persona2-PSX-asm-sources/2_EP/EP_main_grouped.asm
2025-11-07 10:51:48 +05:00

494 lines
28 KiB
NASM
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/////////////////////////////////////////////////////////////////////////////////
//
// Persona 2 Eternal Punishment (PSX) JAP / Custom Characters/Data Patch
// Author: Sergey Shemet 05/11/2025
//
// v 1.1 - Grouped Chars VRAM Rendering
//
.psx
// Определения функций
.definelabel SetDrawTPage, 0x800578fc
.definelabel storeColor, 0x8001c0b4
.definelabel initCopyCharChain, 0x8001b110
.definelabel MakeShadowSmallChar, 0x8001b2a8
.definelabel PrintBigDMAText, 0x8001a3a8
.definelabel MyAddr, 0x8009
.open "2_EP/BIN/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 ra, 0x48(sp) // Сохраняем регистры
sw s0, 0x28(sp)
sw s1, 0x2c(sp)
sw s2, 0x30(sp)
sw s3, 0x34(sp)
sw s4, 0x38(sp) // Сохраняем регистры
sw s5, 0x3c(sp)
sw s6, 0x40(sp)
sw s7, 0x44(sp)
sw s8, 0x24(sp)
move s6, a0 // Адрес чтения текста -> s6
move s1, a1
move s2, a2
// Смещаем поинтер чтения
andi v0, v0, 0xff // Берем младший байт
addiu s6, s6, 0x2 // Сдвигаем указатель текста на +2 байта
lui s3, MyAddr
sh v0, -0x10(s3) // Сохраняем счетчик байтов @ 8008fff0 для общего количества
sh v0, -0x0e(s3) // Сохраняем счетчик байтов @ 8008fff2 для обратного отсчёта
lui t0, 0xff
ori t0, t0, 0xffff // t0 = 00FFFFFF
sw s0, 0x28(sp) // Сохраняем s0 в стеке
andi s0, a3, 0xff // s0 - режим текста (цвет и тень)
move a0, s0 // a0 = s0 (обрезанный цвет)
lui a3, 0x8008 // a3 = 80080000
lui a2, 0xff00 // a2 = FF000000
//Начинаем формировать цепочки
addiu t1, a3, -0x31a8 // t1 = 8007ce68 - Второй счётчик цепочек (корорый меньше)
lw v1, 0x0(t1) // v1 = загружаем свободную цепочку
lbu a1, 0x60(sp) // Флаг тени из стека -> a1
lui v0, 0x8000 // v0 = 80000000
and a2, v1, a2 // a2 = v1 & ff000000
andi a1, a1, 0xff // Очищаем режим текста
and v1, v1, t0 // v1 & 00ffffff
or s4, v1, v0 // s5 - основной адрес свободной цепочки
lw v0, 0x4(t1) // Загружаем счетчик DMA
lw v1, 0x0(s4) // v1 = следующий свободный адрес в цепочке
addiu v0, v0, -0x1 // Уменьшаем счетчик DMA
and v1, v1, t0 // v1 & 00ffffff
or a2, a2, v1 // a2 | v1 = следующая свободная цепочка
sw v0, 0x4(t1) // Сохраняем счетчик DMA
jal storeColor //Сохраняем цвет
sw a2, 0x0(t1) // Сохраняем следующую свободную цепочку
move a0, s4 // Текущий адрес цепочки как параметр инициализации (a0)
sll a1, s1, 0x10 // a1 = X << 16
sra a1, a1, 0x10 // a1 = X координата
sll a2, s2, 0x10 // a2 = Y << 16
sra a2, a2, 0x10 // a2 = Y координата
jal initCopyCharChain // Инициализация цепочки символов (в a0 адрес, a1 - X, a2 - Y)
move a3, s0 //Режим
cputovram_scratchpad_template:
// Инициализация ScratchPad (CPU to VRAM)
lui a3, 0x1f80
ori a3, a3, 0x0348 // a3 = CpuToVram cmd template addr
lui a1, 0x0F00 // Количество команд без flushcache
sw a1, 0x0(a3) // Сохраняем количество команд
lui a0, 0xa000 // a0 = a0000000
sw a0, 0x4(a3)
lui v0, 0x01f0
ori v0, 0x0130
sw v0, 0x8(a3) // сохраняем VU для CPUtoVRAM
lui v0, 0xc
ori v0, 0x2
sw v0, 0xc(a3) // сохраняем 000c0002 (ширина в 16-битных пикселях)
_1bppTo4bpp_table_template:
// Копирование таблицы преобразования 1bpp в 4bpp
lui t8, 0x1f80
ori t8, t8, 0x390
lui t7, 0x8001
ori t7, t7, 0x5fc
lw t5, 0x0(t7)
lw t6, 0x4(t7)
sw t5, 0x0(t8)
sw t6, 0x4(t8)
lw t5, 0x8(t7)
lw t6, 0xC(t7)
sw t5, 0x8(t8)
sw t6, 0xC(t8)
lw t5, 0x10(t7)
lw t6, 0x14(t7)
sw t5, 0x10(t8)
sw t6, 0x14(t8)
lw t5, 0x18(t7)
lw t6, 0x1C(t7)
sw t5, 0x18(t8)
sw t6, 0x1C(t8)
rect_scratchpad_template:
// Шаблон команды rect в scratchpad
lui s3, 0x1f80
ori s3, 0x03d0 // s3 = 1f8003d0 - адрес rect в scratchpad
lui v0, 0x0400
sw v0, 0x0(s3) // Длина цепочки = 4
lui v0, 0x6480
ori v0, 0x8080
sw v0, 0x4(s3)
li v0, 0xf0c0
sh v0, 0xc(s3) // Сохраняем координаты VU (всегда f0c0)
lhu v1, 0xa0(gp) // Загружаем CLUT из GP (всегда 7FD3)
li v0, 0
sw v0, 0x8(s3) //Чистим экранные координаты
sh s2, 0xa(s3) //И сразу устанавливаем Y
lui v0, 0xc
ori v0, 0x8 // Ширина спрайта = 8
sw v0, 0x10(s3) // Сохраняем ширину
sh v1, 0xe(s3) // Сохраняем CLUT
make_sprite_size_table:
lui s3, 0x1f80
ori s3, 0x03f0 // Будем серить в scratch
lui v0, 0x0403 // таблицей рассчитанных команд и ширин
ori v0, 0x0201 // необходимых для спрайтов линии (cmdCount << 2)
sw v0, 0x0(s3)
lui v1, 0x0706
ori v1, 0x0504
sw v1, 0x4(s3)
li v0, 0x0807
sh v0, 0x8(s3)
misc_init:
move t6, s1 // Текущий экранный X, который будет смещаться и писаться в команду rect
// Устанавливаем, исходя из прилетевшей координаты X
TextGroupReadLoop:
// Смотрим количество символов до предела (не более 10) (проверяем MyAddr-0xE)
// Отнимаем количество символов в MyAddr-0xE. Устанавливаем признак, если ещё не конец
// Копируем символы во временный буфер MyAddr (-0x0с)
lui s3, 0x1f80
ori s3, 0x03f0 // Перезадаём адрес таблицы количества спрайтов для нового цикла
lui t1, MyAddr //Постоянный счётчик
lh v1, -0x0e(t1) //Загружаем количество оставшихся символов
lui t2, MyAddr //Сдвиг для буфера
sltiu a0, v1, 0x0B //Меньше 10 в строке?
bne a0, zero, readTextToBuffer
clear s2 //Обнуляем счётчик символов
li v1, 0xA //Установка максимального количество символов
readTextToBuffer:
lbu a0, 0x0(s6) // Основное чтение байта текста
addiu s6, s6, 0x1 // Сдвиг адреса чтения на +1 байт
addiu v1, v1, -0x1 // Осталось в этом блоке...
sb a0, -0x0c(t2) // Запись символа в буфер
addiu s2, s2, 0x1 // Счётчик прочитанного +1
lh v0, -0x0e(t1) // Грузим общий счётчик
addiu t2, t2, 0x1 // Сдвиг записи +1
addiu v0, v0, -0x01 // Общий счётчик -1
bne v1, zero, readTextToBuffer // Проверяем частный счётчик != 0
sh v0, -0x0e(t1) // Сохраняем общий счётчик
li a0, 0
sb a0, -0x0c(t2) //На всякий случай сохраняем 00-терминатор
addu s3, s3, s2 // получаем адрес количества спрайтов из таблицы
addiu s3, -0x1 // index -1
lbu a1, 0x0(s3) // Читаем количество в a1
nop
move t5, a1 // Храним количество блоков для спрайта
lui t3, 0x1f80
ori t3, t3, 0x1c0 // Начало данных спрайта в scratch
form_char_data_in_scratch:
jal make_char_line_in_scratch //Вызываем процедуру формирования строки
move a0, s2 //Передаём в неё длину строки в символах (a0) и в спрайтах (a1)
// (формируем спрайт размером до 60x12)
rect_cmd_init:
lui s3, 0x1f80
ori s3, 0x03d0 // s3 = 1f8003d0 - адрес rect в scratchpad
lui s1, 0x8008 // Загружаем 80080000
lui s0, 0xff
ori s0, s0, 0xffff // s0 = 00ffffff
lui s7, 0x8000 // s7 = 80000000
lui t2, 0xff00 // t2 = ff000000
move t4, s4 // t4 - указатель на print params ОСНОВНОЙ
move t1, s4 // t1 - print params для смещения страниц
rect_set_X:
sh t6, 0x8(s3) // Сохраняем X координату в текущий rect
sll v0, s2, 1
addu v0, s2 // Умножаем количество обработанных символов на 6
sll v0, 1
addu t6, v0 // Сдвигаем X (прочитаем со следующим блоком символов)
rect_set_width:
sll v0, t5, 3 //Количество блоков спрайта * 8 = ширина спрайта
sh v0, 0x10(s3) // Сохраняем ширину спрайта
PageLoop:
////// Балансировщик нагрузки выбора цепочек для равномерного размещения команд (2 команды в 7ce58 + 1 команда в 7ce68)
//////////////////////////////////////// TODO: CHAIN BALANCER
addiu s5, s1, -0x3198 // s5 = 7ce68 (Второй свободный адрес dma)
//s5 - содержит указатель на адрес следующей свободной цепочки. В зависимости от балансировщика может быть 7ce58 или 7ce68
// Создание цепочки копирования спрайта
lw v0, 0x0(s5) // v0 = *адрес свободной цепочки
nop
and a0, v0, t2 // a0 = v0 & ff00..
and v0, v0, s0 // v0 & 00FFFFFF
or a1, v0, s7 // a1 = v0 | s7 (80000000)
lw v0, 0x4(s5) // Счетчик команд
lw v1, 0x0(a1) // Новый адрес свободной цепочки
addiu v0, v0, -0x1 // Уменьшаем счетчик
and v1, v1, s0 // v1 & 00FFFFFF
or a0, a0, v1 // a0 & v1
sw v0, 0x4(s5) // Сохраняем счетчик DMA
sw a0, 0x0(s5) // Сохраняем новую свободную цепочку
rect_copy_scratch_to_ram:
lw t8, 0x0(s3) // Количество команд DMA
lw t7, 0x4(s3) // Команда копирования спрайта
sw t8, 0x0(a1)
sw t7, 0x4(a1)
lw a3, 0x8(s3) // Координаты экрана XY
lw t7, 0xc(s3) // CLUT и координаты текстуры
sw a3, 0x8(a1)
sw t7, 0xc(a1)
lw t8, 0x10(s3) // Размер спрайта после копирования
nop
sw t8, 0x10(a1) // Сохраняем последнюю команду
rect_scratch_connect:
lw v1, 0x18(t1) // Загружаем адрес след цепочки из print params (cur page)
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) // Загружаем адрес след цепочки из print params (cur page)
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) // Сохраняем адрес след актуальной цепочки в print params (cur page)
lhu v0, 0x4(t4) // Проверяем счетчик символов в print params
nop
bne v0, zero, cpu2vram_cmd_loop // Если счетчик символов 0, переходим (уже инициализировали)
clear s8 // Сбрасываем счётчик команд спрайтов
sw a1, 0x28(t1) // Устанавливаем адрес начала цепочки символов в print_params (для хранения значения "цепочка от адреса...")
sw a3, 0xc(t4) // Сохраняем экранные координаты в print params
cpu2vram_cmd_loop:
// Цикл команд cpu_to_vram (формирование спрайтов текста в VRAM)
////// Балансировщик нагрузки выбора цепочек для равномерного размещения команд (2 команды в 7ce58 + 1 команда в 7ce68)
//////////////////////////////////////// TODO: CHAIN BALANCER
addiu s5, s1, -0x31a8 // s5 = 7ce58 (Первый свободный адрес dma)
//s5 - содержит указатель на адрес следующей свободной цепочки. В зависимости от балансировщика может быть 7ce58 или 7ce68
lw v0, 0x0(s5) // Следующая свободная цепочка 7ce58 ()
nop
and a0, v0, t2 // a0 = v0 & FF000000
and v0, v0, s0 // v0 & 00FFFFFF
or a3, v0, s7 // a3 = следующая цепочка & 80...
move a2, a3 // a2 = a3 (следующая цепочка)
lw v0, 0x4(s5) // Счетчик команд
lw v1, 0x0(a3) // Новый адрес свободной цепочки
addiu v0, v0, -0x1 // Уменьшаем счетчик
and v1, v1, s0 // v1 & 00FFFFFF
or a0, a0, v1 // a0 & v1
sw v0, 0x4(s5) // Сохраняем счетчик DMA
sw a0, 0x0(s5) // Сохраняем новую свободную цепочку
//Копируем шаблон cpu2vram
lui a1, 0x1f80
ori a1, a1, 0x348 // 1f800348 = Scratch команда cpu2vram
lw t8, 0x0(a1)
lw t7, 0x4(a1)
sw t8, 0x0(a2)
sw t7, 0x4(a2)
lw t8, 0x8(a1) //Сохраняем базу команды cpu2vram
lw t7, 0xc(a1)
sw t8, 0x8(a2)
sw t7, 0xc(a2)
sll v1, s8, 1 //Текущий индекс блока * 2
addiu v0, v1, 0x130 //складываем со 0x130 (X начала спрайта в VRAM)
sh v0, 0x8(a2) //Обновляем VU X прямо в RAM
copy_char_data_start:
lui t3, 0x1f80
ori t3, t3, 0x1c0 // Начало данных спрайта в scratch
addiu a2, 0x10 //Адрес для записи данных после заголовка команды
clear v0 //Основной счётчик строк (0-11)
sll v1, t5, 2 //v1 = количество байт в одной строке (блоки * 4) (инкремент)
sll a1, s8, 2 //Вычисляем смещение начала чтения
addu a1, t3 //a1 = адрес начала чтения в scratch
copy_char_data_loop:
lw t7, 0x0(a1) //Грузим данные
addu a1, v1 //Смещаем адрес чтения
sw t7, 0x0(a2) //Сохраняем данные
addiu a2, 0x04 //Смещаем адрес записи
addiu v0, 0x01 //Инкремент счётчика строк
bne v0, 0x0c, copy_char_data_loop
nop
bne s8, zero, cpu2vram_dma_link // Если это первая команда (последняя выполняемая, то добавляем flushcache)
nop
set_flush_cache_cmd:
li t8, 0x10
sb t8, -0x3d(a2) //Обновляем длину команды до 0x10
lui t8, 0x0100
sw t8, 0x0(a2) //Устанавливаем команду flush cache в конце команды
cpu2vram_dma_link:
// Связывание предыдущей команды с текущей (формирование ссылки в заголовке команды)
lw v1, 0x18(t1) // v1 <- 18+(t1) - указатель на конец цепочки в текущей странице)
lw v0, 0x0(a3) // v0 <- 0+(a3) - снова читаем первую команду (в данном случае - количество команд)
lw v1, 0x0(v1) // Разыменование указателя на след свободный адрес в цепочке (из print_params)
and v0, v0, t2 // v0 & FF..... Чистим количество команд
and v1, v1, s0 // v1 & 00FFFFFF - чистим след свободный адрес
or v0, v0, v1 // Совмещаем ссылку - Cmd+ADDR
sw v0, 0x0(a3) // Сохраняем ссылку в цепочку
//Связывание текущей команды со следующей (формирование ссылки в заголовке команды + запись в print params)
lw a0, 0x18(t1) // v1 <- 18+(t1) - указатель на конец цепочки в текущей странице)
nop
lw v0, 0x0(a0) // Читаем то, на что указывает указатель (след адрес в цепочке из цепочки)
and v1, a3, s0 // v1 = a3 & 00FFFFFF - от следующего адреса свободной команды отрезаем 80
and v0, v0, t2 // v0 & FF000000 (количество команд) от след адреса
or v0, v0, v1 // Совмещаем ссылку - Cmd+ADDR
sw v0, 0x0(a0) // Сохраняем ссылку в цепочку
sw a3, 0x18(t1) // Сохраняем адрес последней команды в print params
//Конец цикла команд cpu_to_vram
addiu s8, 0x1 //Смешаем индекс читаемого спрайта
bne s8, t5, cpu2vram_cmd_loop //Проверка на достижение количества спрайтов
nop
addiu t1, t1, 0x4 // Сдвигаем print params для следующей страницы
addiu v0, t4, 0x8 // v0 = t4 + 8 (t1 не должен быть больше, чем t4 + 4)
sltu v0, t1, v0 // Проверяем прохождение первой страницы
bne v0, zero, PageLoop // Если не прошли, продолжаем цикл страниц
nop
chunk_making_end:
// Увеличиваем счетчик символов + проверка
lhu v0, 0x4(s4) // Текущее количество спрайтов
lui t3, MyAddr
addiu v0, v0, 0x1 // Увеличиваем на 1
lhu v1, -0x0e(t3) // Количество оставшихся символов
sh v0, 0x4(s4) // Сохраняем счетчик спрайтов в print_params
bne v1, zero, TextGroupReadLoop
// Если обработаны все буквы и созданы все спрайты, то выходим
nop
TextEnd:
clear s2
lui s3, 0x8008
addiu s5, s3, -0x3198 // s5 = 7ce68 (Второй свободный адрес dma)
lui s1, 0xff
ori s1, s1, 0xffff // s1 = 00ffffff
move s0, s4 // s0 = Указатель на print params (теперь они и в t1, s4 и s0)
clear a1
PageTLoop:
//Добавляем команду переключения текстурной страницы (SetDrawTPage) в обе страницы
li a2, 0x1 // a2 = 1
addu s2, s2, a2 // Увеличиваем счетчик страниц
lui v0, 0x8000 // v0 = 80000000
lui a0, 0xff00 // a0 = ff000000
lw v1, 0x0(s5) // Следующая свободная цепочка
lw a3, 0x9c(gp) // Текстурная страница из параметров
and v1, v1, s1 // v1 & 00FFFFFF
or v1, v1, v0 // v1 | v0 = 80.....
sw v1, 0x30(s0) // Сохраняем последний адрес в print params
//Линкуем адреса
lw v0, 0x0(s5) // Следующая свободная цепочка ещё раз!
lw v1, 0x0(v1) // разыменование
and v0, v0, a0 // v0 & ff000000
and v1, v1, s1 // v1 & 00FFFFFF
lw a0, 0x4(s5) // Счетчик DMA
or v0, v0, v1 // v0 | v1
sw v0, 0x0(s5) // Сохраняем свободный адрес
addiu a0, -0x1 // Уменьшаем счетчик DMA
sw a0, 0x4(s5) // Сохраняем счетчик DMA
lw a0, 0x30(s0) // Читаем адрес команды в a0 для передачи в функцию установки texPage
jal SetDrawTPage // Устанавливаем страницу отрисовки
addiu s0, s0, 0x4 // Смещаем print params для следующей страницы
sltiu v0, s2, 0x2 // Проверяем счетчик < 2
bne v0, zero, PageTLoop // Если да, продолжаем цикл
clear a1 // a1 = 0
move v0, s4 // Возвращаем основные параметры 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)
lw s8, 0x24(sp) //need to be saved!
jr ra // Возврат
addiu sp, sp, 0x50 // Восстанавливаем стек
.include "2_EP/EP_charload_grouped.asm"
.include "2_EP/EP_charCalcs.asm"
.close
.include "2_EP/EP_txtpatches.asm" // misc text patches
// compile with ./armips -sym 2_EP/BUILD_LOGS/SLPS_028.25.map -temp 2_EP/BUILD_LOGS/SLPS_028.25.txt 2_EP/EP_main_grouped.asm