EP: Groped chars v1.1

+ char calcs calls
This commit is contained in:
sShemet
2025-11-05 16:24:34 +05:00
parent 3513bcaed3
commit 49f11f6cc8
10 changed files with 1466 additions and 323 deletions

View File

@@ -1,6 +1,10 @@
;
; Persona 2 Eternal Punishment (PSX) JAP / Custom Characters/Data Patch
; Author: Sergey Shemet 06/10/2022
/////////////////////////////////////////////////////////////////////////////////
//
// Persona 2 Eternal Punishment (PSX) JAP / Custom Characters/Data Patch
// Author: Sergey Shemet 05/11/2025
//
// v 1.1 - Grouped Chars VRAM Rendering
//
.psx
@@ -13,12 +17,12 @@
.definelabel MyAddr, 0x8009
.open "SLPS_028.25", 0x8000F800
.open "2_EP/BIN/SLPS_028.25", 0x8000F800
.org 0x80090000
ExternalPrint:
// Текстовая тестовая процедура
lhu v0, 0x0(a0) // Читаем 2 байта текста
nop
srl v1, v0, 0xD // Проверяем 13-й бит (система 1 байт)
@@ -29,366 +33,428 @@ ExternalPrint:
MyPrintLineRoutine:
addiu sp, sp, -0x50 // Выделяем место в стеке
sw s4, 0x38(sp) // Сохраняем регистры
move s4, a0 // Адрес чтения текста -> s4
sw ra, 0x48(sp) // Сохраняем регистры
sw s0, 0x28(sp)
move s0, a1
sw s1, 0x2c(sp)
move s1, a2
sw s2, 0x30(sp)
sw s3, 0x34(sp)
sw s4, 0x38(sp) // Сохраняем регистры
sw s5, 0x3c(sp)
sw s6, 0x40(sp)
sw s7, 0x44(sp)
// Обработка новой команды
move s6, a0 // Адрес чтения текста -> s6
move s1, a1
move s2, a2
// Смещаем поинтер чтения
andi v0, v0, 0xff // Берем младший байт
addiu s4, s4, 0x2 // Сдвигаем указатель текста на +2 байта
addiu s6, s6, 0x2 // Сдвигаем указатель текста на +2 байта
lui s3, MyAddr
sw v0, -0x10(s3) // Сохраняем счетчик байтов @ 8008fff0
sh v0, -0x10(s3) // Сохраняем счетчик байтов @ 8008fff0 для общего количества
sh v0, -0x0e(s3) // Сохраняем счетчик байтов @ 8008fff2 для обратного отсчёта
// Инициализация 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
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 = 8007b6d0 - новая свободная цепочка
lw v1, -0x31a8(a3) // v1 = загружаем свободную цепочку
//Начинаем формировать цепочки
addiu t1, a3, -0x31a8 // t1 = 8007ce68 - Второй счётчик цепочек (корорый меньше)
lw v1, 0x0(t1) // 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)
lui v0, 0x8000 // v0 = 80000000
// Инициализация цепочек
and a2, v1, a2 // a2 = v1 & ff000000
and a2, v1, a2 // a2 = v1 & ff000000
andi a1, a1, 0xff // Очищаем режим текста
and v1, v1, t0 // v1 & 00ffffff
or s5, v1, v0 // s5 - основной адрес свободной цепочки
and v1, v1, t0 // v1 & 00ffffff
or s4, 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 = следующая свободная цепочка
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
jal storeColor //Сохраняем цвет
sw a2, 0x0(t1) // Сохраняем следующую свободную цепочку
move a0, s4 // Текущий адрес цепочки
sll a1, s1, 0x10 // a1 = X << 16
sra a1, a1, 0x10 // a1 = X координата
sll a2, s1, 0x10 // a2 = Y << 16
jal initCopyCharChain // Инициализация цепочек символов
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 a2, 0x1f80
ori a2, a2, 0x348 // a2 = первый символ начала
lui a3, 0x1f80
ori a3, a3, 0x0348 // a3 = CpuToVram cmd template addr
// 4-битная таблица паттернов
.include "EP_4bitPattern.asm"
lui a1, 0x0F00 // Количество команд без flushcache
sw a1, 0x0(a3) // Сохраняем количество команд
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 a0, 0xa000 // a0 = a0000000
sw a0, 0x4(a3)
// Инициализация символа
lui v0, 0x100 // Сброс кэша 01000000
sb a0, 0x3(a2) // Сохраняем количество команд
sw v1, 0x4(a2) // Сохраняем a0000000
lui v0, 0x01f0
ori v0, 0x0130
sw v0, 0x8(a3) // сохраняем VU для CPUtoVRAM
lui at, 0x1f80 // Начало scratchpad
sw v0, 0x388(at) // Сохраняем в конец первой цепочки символов
lui v0, 0xc
ori v0, 0x2
sw v0, 0xc(a3) // сохраняем 000c0002 (ширина в 16-битных пикселях)
// Формирование команды DMA копирования спрайта
li v0, 0x4
sb v0, 0x3(s3) // Длина цепочки = 4
li v0, 0x64
sb v0, 0x7(s3) // Команда копирования спрайта
_1bppTo4bpp_table_template:
// Копирование таблицы преобразования 1bpp в 4bpp
lui t8, 0x1f80
ori t8, t8, 0x390
lui t7, 0x8001
ori t7, t7, 0x3e8
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)
lhu a0, 0x98(gp) // Загружаем X координату
lhu v1, 0x9a(gp) // Загружаем Y координату
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, 0x9a(gp) // Загружаем CLUT из GP (всегда 7FD3)
li v0, 0
sw v0, 0x8(s3) //Чистим экранные координаты
sh s2, 0xa(s3) //И сразу устанавливаем Y
li v0, 0x80
sb v0, 0x4(s3)
sb v0, 0x5(s3)
sb v0, 0x6(s3) // Формируем 80808064
lui v0, 0xc
ori v0, 0x8 // Ширина спрайта = 8
sw v0, 0x10(s3) // Сохраняем ширину
sh v1, 0xe(s3) // Сохраняем CLUT
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) // Сохраняем в первый символ
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)
bgez a3, LAB_800194fc
misc_init:
move t6, s1 // Текущий экранный X, который будет смещаться и писаться в команду rect
// Устанавливаем, исходя из прилетевшей координаты X
SetX1:
sh s1, 0x20(sp) // Сохраняем X половину в sp+20
addiu t0, a3, 0x3f
TextGroupReadLoop:
// Смотрим количество символов до предела (не более 10) (проверяем MyAddr-0xE)
// Отнимаем количество символов в MyAddr-0xE. Устанавливаем признак, если ещё не конец
// Копируем символы во временный буфер MyAddr (-0x0с)
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
lui s3, 0x1f80
ori s3, 0x03f0 // Перезадаём адрес таблицы количества спрайтов для нового цикла
lui t1, MyAddr //Постоянный счётчик
LAB_80019520:
sra v0, v0, 0x8
sll v0, v0, 0x8
subu v0, v1, v0
sb v0, 0xd(s3) // Сохраняем Y байт спрайта символа
lhu v1, 0xa0(gp)
lh v1, -0x0e(t1) //Загружаем количество оставшихся символов
lui t2, MyAddr //Сдвиг для буфера
sltiu a0, v1, 0x0B //Меньше 10 в строке?
bne a0, zero, readTextToBuffer
clear s2 //Обнуляем счётчик символов
li v1, 0xA //Установка максимального количество символов
li v0, 0x8 // Ширина спрайта = 8
sh v0, 0x10(s3) // Сохраняем ширину
sh v1, 0xe(s3) // Сохраняем значение
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-терминатор
sh s0, 0x8(s3) // Сохраняем X
sh s1, 0xa(s3) // Сохраняем Y
addu s3, s3, s2 // получаем адрес количества спрайтов из таблицы
addiu s3, -0x1 // index -1
lbu a1, 0x0(s3) // Читаем количество в a1
nop
move t5, a1 // Храним количество блоков для спрайта
li v0, 0xc // Высота спрайта = 12
sh v0, 0x12(s3) // Сохраняем высоту
lui t3, 0x1f80
ori t3, t3, 0x1c0 // Начало данных спрайта в scratch
// Начало чтения текста
lbu a0, 0x0(s4) // Основное чтение байта команды
nop
addiu s4, s4, 0x1 // Сдвиг адреса чтения на +1 байт
form_char_data_in_scratch:
jal make_char_line_in_scratch //Вызываем процедуру формирования строки
move a0, s2 //Передаём в неё длину строки в символах (a0) и в спрайтах (a1)
// (формируем спрайт размером до 60x12)
SpaceCheckBranch:
jal makeCharPixelsCustom // Создание маленького символа в scratch
clear s2
rect_cmd_init:
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 // Параметры снова
lui s3, 0x1f80
ori s3, 0x03d0 // s3 = 1f8003d0 - адрес rect в scratchpad
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) // Сохраняем новую свободную цепочку
lui s1, 0x8008 // Загружаем 80080000
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) // Сохраняем последнюю команду
lui s0, 0xff
ori s0, s0, 0xffff // s0 = 00ffffff
lui s7, 0x8000 // s7 = 80000000
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
lui t2, 0xff00 // t2 = ff000000
move t4, s4 // t4 - указатель на print params ОСНОВНОЙ
move t1, s4 // t1 - print params для смещения страниц
sw a1, 0x28(t1) // Устанавливаем первую цепочку символов
sw t8, 0xc(t6) // Сохраняем координаты в параметры
// sh s2, 0xe(t4) // Сохраняем половину Y
rect_set_X:
sh t6, 0x8(s3) // Сохраняем X координату в текущий rect
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) // Сохраняем новую свободную цепочку
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) // Сохраняем ширину спрайта
beq v0, zero, Copy16Bytes // Если MOD 4 = 0, ветвление
addiu v0, a1, 0x40
PageLoop:
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
////// Балансировщик нагрузки выбора цепочек для равномерного размещения команд (2 команды в 7ce58 + 1 команда в 7ce68)
//////////////////////////////////////// TODO: CHAIN BALANCER
addiu s5, s1, -0x3198 // s5 = 7ce68 (Второй свободный адрес dma)
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
//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) // Сохраняем новую свободную цепочку
last4BytesCopy:
// Копируем последние 4 байта
lwl t9, 0x3(a1)
lwr t9, 0x0(a1)
nop
swl t9, 0x3(a2)
swr t9, 0x0(a2)
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) // Сохраняем последнюю команду
// Связывание 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
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) // Обновляем текущий адрес цепочки
// Сдвиг 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
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 // Сбрасываем счётчик команд спрайтов
SpaceJump:
// Увеличиваем счетчик символов + проверка
lhu v0, 0x4(s5) // Текущее количество символов DMA
lui t9, MyAddr
addiu v0, v0, 0x1 // Увеличиваем на 1
sh v0, 0x4(s5) // Сохраняем счетчик в цепочку
sw a1, 0x28(t1) // Устанавливаем адрес начала цепочки символов в print_params (для хранения значения "цепочка от адреса...")
sw a3, 0xc(t4) // Сохраняем экранные координаты в print params
// Проверка переполнения 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 // Сдвигаем адрес символа
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
bne v0, v1, NextChar // Если текущий символ общему количеству, продолжаем
nop
TextEnd:
clear s2
lui s3, 0x8008
addiu s4, s3, -0x3198 // s4 = 7b6d0
addiu s5, s3, -0x3198 // s5 = 7ce68 (Второй свободный адрес dma)
lui s1, 0xff
ori s1, s1, 0xffff // s1 = 00ffffff
move s0, s5 // s0 = текущие параметры строки
move s0, s4 // s0 = Указатель на print params (теперь они и в t1, s4 и s0)
clear a1
PageTLoop:
// Завершающая обработка страниц
//Добавляем команду переключения текстурной страницы (SetDrawTPage) в обе страницы
li a2, 0x1 // a2 = 1
addu s2, s2, a2 // Увеличиваем счетчик
addu s2, s2, a2 // Увеличиваем счетчик страниц
lui v0, 0x8000 // v0 = 80000000
lui a0, 0xff00 // a0 = ff000000
lw v1, -0x3198(s3) // Следующая свободная цепочка
lw a3, 0x9c(gp) // Некоторые параметры (почти DMA)
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) // Сохраняем последний свободный адрес в параметрах DMA
lw v0, -0x3198(s3) // Следующая свободная цепочка
lw v1, 0x0(v1) // Создаем еще одну цепочку
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(s4) // Счетчик DMA
lw a0, 0x4(s5) // Счетчик 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
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 // Сдвигаем параметры DMA для следующей страницы
addiu s0, s0, 0x4 // Смещаем print params для следующей страницы
sltiu v0, s2, 0x2 // Проверяем счетчик < 2
bne v0, zero, PageTLoop // Если да, продолжаем цикл
clear a1 // a1 = 0
@@ -407,4 +473,16 @@ PageTLoop:
lw s0, 0x28(sp)
jr ra // Возврат
addiu sp, sp, 0x50 // Восстанавливаем стек
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