620 lines
37 KiB
NASM
620 lines
37 KiB
NASM
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// Persona 2 Eternal Punishment CD Extra (PSX) JAP / Custom Characters/Data Patch
|
||
//
|
||
// Author: Sergey Shemet 17/09/2025
|
||
//
|
||
|
||
.psx
|
||
|
||
.definelabel SetDrawTPage, 0x800522F0
|
||
.definelabel storeColor, 0x8001ADC8
|
||
.definelabel initCopyCharChain, 0x80019D70
|
||
.definelabel MakeShadowSmallChar, 0x80019F0C // 0x800196BC -big char shadow???
|
||
.definelabel OriginalKanjiPrint, 0x80019000
|
||
|
||
.definelabel MyAddr, 0x8009
|
||
|
||
.open "SLPS_028.26", 0x8000F800
|
||
|
||
.org 0x80090000
|
||
|
||
ExternalPrint:
|
||
// Text routing test routine
|
||
move r3,s3
|
||
move s3,r4
|
||
|
||
lhu v0,0x0(s3) // Read half 2 bytes of text
|
||
move s3,r3
|
||
srl v1,v0,0xD // Check 13th bit (Russian text format 20XX cmd)
|
||
bne v1,zero,MyPrintLineRoutine
|
||
clear v1
|
||
j OriginalKanjiPrint // Если не включен 13 бит, то это обычный японский текст. Вызов обычной подпрограммы вывода.
|
||
nop
|
||
|
||
MyPrintLineRoutine:
|
||
addiu sp, sp, -0x50 // Выделение места в стеке (80 bytes)
|
||
sw s6, 0x40(sp) // Сохранение s6
|
||
move s6, a0 // s6 = param_1 (указатель на текст)
|
||
sw s1, 0x2C(sp) // Сохранение s1
|
||
move s1, a1 // s1 = param_2 (X координата)
|
||
sw s2, 0x30(sp) // Сохранение s2
|
||
move s2, a2 // s2 = param_3 (Y координата)
|
||
sw s3, 0x34(sp) // Сохранение s3
|
||
|
||
|
||
|
||
//v1/r3 - MyChars charcount
|
||
andi v0, 0xff
|
||
addiu s6, 0x2 //Moving text read pointer +2bytes
|
||
|
||
|
||
lui s3, MyAddr
|
||
sh v0, -0x10(s3) //store half bytecounter @ 8008fff0
|
||
sh v0, -0x0E(s3) //store half bytecounter @ 8008fff2
|
||
|
||
//////////////////////////////////////
|
||
|
||
// Инициализация адресов scratch и масок
|
||
lui s3, 0x1F80 // Базовый адрес GPU
|
||
ori s3, s3, 0x03D0 // Адрес для команд отрисовки
|
||
|
||
lui t0, 0xFF // Маска для DMA операций
|
||
ori t0, t0, 0xFFFF
|
||
|
||
sw s0, 0x28(sp) // Сохранение s0
|
||
andi s0, a3, 0xFF // s0 = param_4 (цвет/атрибуты)
|
||
move a0, s0 // a0 = цвет текста
|
||
|
||
// Блок инициализации DMA
|
||
InitDMA:
|
||
update_free_dma:
|
||
lui a3, 0x8008 // Базовый адрес для текстового DMA
|
||
lui a2, 0xFF00 // Маска для адресной части
|
||
addiu t1, a3, -0x6C20 // TextDMANexChainAddr
|
||
lw v1, -0x6C20(a3) // Загрузка текущего DMA адреса
|
||
lbu a1, 0x60(sp) // Загрузка цвета из стека
|
||
lui v0, 0x8000 // Флаг для адресации
|
||
|
||
// Сохранение всех регистров
|
||
sw ra, 0x4C(sp) // Сохранение return address
|
||
sw s8, 0x48(sp) // Сохранение s8
|
||
sw s7, 0x44(sp) // Сохранение s7
|
||
sw s5, 0x3C(sp) // Сохранение s5
|
||
sw s4, 0x38(sp) // Сохранение s4
|
||
|
||
// Подготовка DMA цепочки
|
||
PrepareDMAChain:
|
||
and a2, v1, a2 // Извлечение первого байта
|
||
and v1, v1, t0 // Отсекаем первый байт у адреса
|
||
or s4, v1, v0 // Совмещаем 80...... и 00ADDR = новый DMA адрес
|
||
|
||
lw v0, 0x4(t1) // TextDMACounter
|
||
lw v1, 0x0(s4) // Разыменовывание указателя на текущий адрес = след адрес
|
||
addiu v0, v0, -1 // Декремент счетчика
|
||
and v1, v1, t0 // Очистка адресной части
|
||
or a2, a2, v1 // Объединение исходного первого байта (FF) с новым адресом
|
||
sw v0, 0x4(t1) // Сохранение счетчика
|
||
|
||
// Подготовка атрибутов строки
|
||
PrepareAttrs:
|
||
andi a1, a1, 0xFF // Маскирование цвета
|
||
jal storeColor // Установка цвета и тени
|
||
sw a2, -0x6C20(a3) // Сохранение нового свободного DMA адреса
|
||
move a0, s4 // a0 = DMA адрес
|
||
|
||
// Подготовка координат
|
||
sll a1, s1, 0x10 // Подготовка X координаты
|
||
sra a1, a1, 0x10
|
||
sll a2, s2, 0x10 // Подготовка Y координаты
|
||
sra a2, a2, 0x10
|
||
jal initCopyCharChain // Инициализация строки
|
||
move a3, s0 // a3 = а-три-буты
|
||
|
||
init_cputovram_vars_int_scratch:
|
||
lui a3, 0x1F80
|
||
ori a3, a3, 0x0348 // Адрес паттернов в scratchpad
|
||
//lui a2, 0x1F80
|
||
//ori a2, a2, 0x038C // Disable second kanji half command
|
||
|
||
li v0, 0x02 // Ширина в px в 16битном режиме (Original 3px (12 in 4bit), my - 2px)
|
||
sh v0, 0x1C(sp)
|
||
li v0, 0x0C // Высота полукандзи. (Original 6px, my - 12px)
|
||
li a1, 0x0D // 0D - оригинальная длина команды CPUtoVRAM (Scratch) в words
|
||
lui a0, 0xA000 // A0000000 - CPUtoVRAM command
|
||
|
||
sh v0, 0x1E(sp) // sp+1c = 000C0002
|
||
li a1, 0x10 //INSERT // Переписываем длину команды (т.к. формируем символ в пределах одной команды)
|
||
sb a1, 0x3(a3) // Установка длины 0x10 элемента цепочки CPUtoVRAM (Scratch)
|
||
sw a0, 0x4(a3) // Установка команды A0000000 CPUtoVRAM (Scratch)
|
||
|
||
lhu t0, 0x9C(gp) // Загрузка vram X из глобальных настроек
|
||
lhu v0, 0x9E(gp) // Загрузка vram Y из глобальных настроек
|
||
lui v1, 0x0100 // GPU flushcache command (0100 0000)
|
||
|
||
lui at, 0x1F80
|
||
sw v1, 0x0388(at) // Save flushcache (конец первой команды)(not 37c!)
|
||
// lower half params set
|
||
// sb a1, 0x3(a2) // Отключаем данные нижней половины
|
||
// sw a0, 0x4(a2) //
|
||
|
||
// Настройка координат для каждой половины символа.
|
||
coords_size_cputovram_scr:
|
||
lh a1, 0x9C(gp) // Загрузка X
|
||
move s8, s2 // Сохранение Y позиции
|
||
// lui at, 0x1F80
|
||
// sw v1, 0x03C0(at) // Save flushcache (second command (Disabled))
|
||
|
||
sh t0, 0x18(sp) // Сохранение XY в стек как полуслова (CONCAT)
|
||
sh v0, 0x1A(sp)
|
||
lw a0, 0x18(sp) //Загружаем XY-word из стека для CPUtoVRAM
|
||
//Абсолютно неоптимальная хрень - можно было сразу вытащить word из 0x9C(gp),
|
||
// либо просто задать a0 = 3001F001,
|
||
// так как адрес всегда один и тот же. Скорее всего, он менялся при разработке и дебаге игры
|
||
|
||
// sh v0, 0x1A(sp) //Отключаем вычисление и сохранение VRAM-Y нижнего полусимвола.
|
||
|
||
lw v1, 0x18(sp)
|
||
lw v0, 0x1C(sp) //Грузим размер символа (16-бит) из стека
|
||
move t0, a1 // t0 будет X - пригодится ниже, в расчётах
|
||
sw a0, 0x8(a3) //сохраняем 3001F001 to 00350 (uv-коодинаты в команду cputovram - 130x1f0)
|
||
// sw v0, 0xc(a2) //disable lower
|
||
|
||
sw v0, 0xC(a3) // set 02000c00 to 00354 (sprite pixel size)
|
||
// sw v1, 0x8(a2)
|
||
|
||
lui v0,0x8001 //load 80010000 to r2
|
||
|
||
//4bit pattern in scratch
|
||
|
||
lui t8, 0x1f80
|
||
ori t8, t8, 0x2e0
|
||
addiu t7, v0, 0x3e8
|
||
lwl t5, 0x3(t7)
|
||
lwr t5, 0x0(t7)
|
||
lwl t6, 0x7(t7)
|
||
lwr t6, 0x4(t7)
|
||
swl t5, 0x3(t8)
|
||
swr t5, 0x0(t8)
|
||
swl t6, 0x7(t8)
|
||
swr t6, 0x4(t8)
|
||
lwl t5, 0xb(t7)
|
||
lwr t5, 0x8(t7)
|
||
lwl t6, 0xf(t7)
|
||
lwr t6, 0xc(t7)
|
||
swl t5, 0xb(t8)
|
||
swr t5, 0x8(t8)
|
||
swl t6, 0xf(t8)
|
||
swr t6, 0xc(t8)
|
||
lwl t5, 0x13(t7)
|
||
lwr t5, 0x10(t7)
|
||
lwl t6, 0x17(t7)
|
||
lwr t6, 0x14(t7)
|
||
swl t5, 0x13(t8)
|
||
swr t5, 0x10(t8)
|
||
swl t6, 0x17(t8)
|
||
swr t6, 0x14(t8)
|
||
lwl t5, 0x1b(t7)
|
||
lwr t5, 0x18(t7)
|
||
lwl t6, 0x1f(t7)
|
||
lwr t6, 0x1c(t7)
|
||
swl t5, 0x1b(t8)
|
||
swr t5, 0x18(t8)
|
||
swl t6, 0x1f(t8)
|
||
swr t6, 0x1c(t8)
|
||
|
||
// Copy Sprite DMA Command Forming
|
||
init_rect_cmd_in_scr:
|
||
li v0,0x4 // Длина команды RectTexBlend (04000000)
|
||
sb v0,0x3(s3) // сохраняем в scratch
|
||
li v0,0x64 // Команда RectTexBlend (64)
|
||
sb v0,0x7(s3) // Сохранение команды RectTexBlend (64)
|
||
li v0,0x80 // 80 - стандартная яркость спрайта
|
||
sb v0,0x4(s3) // R
|
||
sb v0,0x5(s3) // G
|
||
sb v0,0x6(s3) // B = Сохранение 80 80 80 + 64 (3d4,5,6,7)
|
||
|
||
//
|
||
// Здесь запаковка ИСХОДНЫХ координат спрайта в VRAM.
|
||
//Но они никогда не меняются. Наверное, можно оптимизировать и записывать f0c0
|
||
PositionProcessing:
|
||
bgez a1, CalculateXOffset // Проверка знака X координаты
|
||
sh s1, 0x20(sp) // Сохранение X позиции
|
||
|
||
AddOffsetX:
|
||
addiu t0, a1, 0x3F // Смещение, если меньше ноля
|
||
|
||
CalculateXOffset:
|
||
sra v0, t0, 0x6
|
||
sll v0, v0, 0x6 // округляем X
|
||
subu v0, a1, v0
|
||
lh v1, 0x9E(gp) // Загрузка Y параметра
|
||
sll v0, v0, 0x2 // Умножение X на 4
|
||
sb v0, 0xC(s3) // Сохранение X в команду Reсt
|
||
|
||
// Обработка Y координаты
|
||
YCoordinateProcessing:
|
||
bgez v1, CalculateYOffset // Проверка знака Y координаты
|
||
move v0, v1
|
||
|
||
AddOffsetY:
|
||
addiu v0, v1, 0xFF // Добавление смещения если отрицательно
|
||
|
||
CalculateYOffset:
|
||
sra v0, v0, 0x8 // Округление координаты Y
|
||
sll v0, v0, 0x8
|
||
subu v0, v1, v0
|
||
sb v0, 0xD(s3) // Сохранение Y текстуры в команду Reсt
|
||
|
||
// всю эту часть кода можно смело заменять записью 7DF3F0C0 в адрес 0xC(s3)
|
||
// она рассчитана на динамический расчёт координат UV и CLUT, но они у нас статические
|
||
|
||
lhu v1, 0xA4(gp) // Загружаем из глобальных настроек координаты CLUT текста в VRAM (ВСЕГДА 7FD3 - 304 x 511)
|
||
li v0, 0x8
|
||
sh v0, 0x10(s3) // Сохраняем ширину спрайта в пикселях
|
||
li v0, 0xc
|
||
sh v0, 0x12(s3) // Сохраняем высоту спрайта в пикселях (0xC = 12)
|
||
sh v1, 0xe(s3) // Сохраняем clut в команду rect
|
||
|
||
// Основной цикл чтения символов из памяти
|
||
LoadCharacter:
|
||
lbu a0, 0x0(s6) // Загрузка символа из текста (Читаем byte вместо half) - текст хранится побайтово
|
||
nop
|
||
|
||
//Неактуальный код проверки терминатора иероглифов
|
||
//sltiu v0, a0, 0x1000 // Проверка на конец текста
|
||
//beq v0, zero, FinishDrawing // Если символ > 0x1000 (команда) - завершаем
|
||
|
||
addiu s6, s6, 0x1 // Переход к следующему символу (в оригинале было смещение на 2 байта)
|
||
|
||
// Подготовка к отрисовке символа
|
||
lui s1, 0x8008
|
||
addiu s5, s1, -0x6C20 // TextDMANexChainAddr
|
||
lui s0, 0xFF
|
||
ori s0, s0, 0xFFFF
|
||
lui s7, 0x8000
|
||
|
||
// Цикл отрисовки символа
|
||
NextChar:
|
||
addiu r4, r4, -0x20 // Смещаем код символа для правильного рассчёта в шрифте
|
||
//Тут нужна проверка на пробел
|
||
|
||
isFirstChar:
|
||
jal makeCharPixelsCustom // Создание символа в scratchpad кастомной процедурой
|
||
clear s2 // Сброс счетчика
|
||
|
||
// Настройка X для символа
|
||
Setup_X_coord_in_rect_scratch:
|
||
lw v0, 0xAC(gp) // Первая координата текста
|
||
lhu v1, 0x4(s4) // Счётчик отрисованных символов в строке (print_params+4)
|
||
addiu v0, v0, 0x6 // Добавление смещения к первой координате
|
||
mult v1, v0 // Умножаем...
|
||
|
||
some_printparams_save:
|
||
lui t2, 0xFF00 // Маска для адреса
|
||
move t4, s4 // Копирование адреса print_params
|
||
addiu t3, sp, 0x10 // Сохраняем адрес стек+0x10
|
||
move t1, s4 // t1 - основной регистр print_params, который смещается для разных страниц
|
||
|
||
end_x_coord_rect:
|
||
sh s8, 0xA(s3) // Сохранение Y позиции в команду Rect (3dc)
|
||
lhu t5, 0x20(sp) // Загрузка X позиции из стека
|
||
mflo t6 // Результат умножения в t6
|
||
addu v0, t5, t6 // Вычисление финальной позиции (X + смещение)
|
||
sh v0, 0x8(s3) // Сохранение X позиции в команду Rect (3d8)
|
||
|
||
PageLoop:
|
||
update_free_dma2:
|
||
// получаем свободный адрес для цепочки в текущей странице
|
||
lw v0, -0x6C20(s1) // Текущий свободный DMA адрес
|
||
nop
|
||
and a0, v0, t2 // В a0 оставляем первый байт с помощью маски & FF000000 (должно получиться 80000000)
|
||
and v0, v0, s0 // В v0 срезаем первый байт с помощью маски & 00FFFFFF
|
||
or a1, v0, s7 // в a1 cовмещаем адрес, приводим к виду (80XXXXXX) - получаем конечный адрес для записи команды rect
|
||
|
||
lw v0, 0x4(s5) // DMA счетчик оставшихся свободных цепочек
|
||
lw v1, 0x0(a1) // Разыменовываем адрес следующей свободной цепочки (v1 = *a1)
|
||
addiu v0, v0, -1 // Декремент счетчика
|
||
and v1, v1, s0 // Очистка адресной части следующей свободной цепочки
|
||
or a0, a0, v1 // Совмещаем адрес с FF......
|
||
sw v0, 0x4(s5) // Сохранение счетчика
|
||
sw a0, -0x6C20(s1) // Сохранение нового адреса
|
||
|
||
// Копирование данных команды rect в RAM из scratch
|
||
CopyRectCmd:
|
||
lw t7, 0x0(s3) // Загрузка данных команды rect из scratch
|
||
lw t8, 0x4(s3)
|
||
lw t5, 0x8(s3)
|
||
lw t6, 0xC(s3)
|
||
|
||
sw t7, 0x0(a1) // Сохранение данных команды rect в RAM
|
||
sw t8, 0x4(a1)
|
||
sw t5, 0x8(a1)
|
||
sw t6, 0xC(a1)
|
||
|
||
lw t7, 0x10(s3)
|
||
nop // ждём команду, чтобы значение загрузилось в регистр (особенность MIPS)
|
||
sw t7, 0x10(a1) // докидываем данные
|
||
|
||
// Линкуем предыдущую команду страницы и текущую ()
|
||
previous_last_cmd_link_with_current:
|
||
lw v1, 0x18(t1) // Загрузка указателя хранения последней команды в странице print_params+0x18
|
||
lw v0, 0x0(a1) // Загружаем текущую длину (0004), которая перезаписала адрес в цепочке DMA, когда копировали из sсratch
|
||
lw v1, 0x0(v1) // Разыменование указателя (v1 = *v1), читаем количество команд последней команды (обычно XXFFFFFF)
|
||
and v0, v0, t2 // Чистим количество команд (& ff)
|
||
and v1, v1, s0 // Очистка адресной части из параметров строки print_params
|
||
or v0, v0, v1 // Объединение (делаем cmdCount + ffffff from next_cmd) - очистка/экранирование?
|
||
sw v0, 0x0(a1) // Сохранение адреса следующей команды в команде rect (04ffffff)
|
||
|
||
lw a0, 0x18(t1) // Снова грузим указатель на маску print_params+0x18 (00fffff)
|
||
nop // ПРОПУСК!
|
||
lw v0, 0x0(a0) // Разыменование указателя (v0 = *a0)
|
||
and v1, a1, s0 // Отрезаем первый байт у адреса цепочки команды rect (сначала это маска 00ФФФФФФ)
|
||
and v0, v0, t2 // &ff000000 (c маской)
|
||
or v0, v0, v1 // Объединение (берём у маски кол-во команд, и след адрес)
|
||
sw v0, 0x0(a0) // Сохраняем след адрес вместо маски (00freeChain)
|
||
sw a1, 0x18(t1) // Сохраняем адрес след свободной цепочки в print_params (t1+18)
|
||
// То, что было маской - стало ссылкой на след команду цепочки в этой странице
|
||
|
||
// Проверка на первый символ (нужна ли запись первой записи в print_params+0x28?)
|
||
lhu v0, 0x4(t4) // Проверка - есть ли хоть один сформированный символ
|
||
nop
|
||
bne v0, zero, NotFirstChar // Если не ноль, пропускаем
|
||
clear t0 // Очистка счетчика
|
||
|
||
sw a1, 0x28(t1) // Установка первичного адреса в print_params (+16 от ссылки на бывшую маску)
|
||
|
||
//ИТОГО -- PrintParams + 0x18 - указатель на последнюю команду на странице
|
||
// PrintParams + 0x28 - указатель на первую команду на странице
|
||
// Для второй страницы - смещаемся +0x04
|
||
|
||
NotFirstChar:
|
||
|
||
move a3, t0 // Обнуляем a3 - это смещение для копирования данных в полусимволе
|
||
move a2, t3 // загрузка адреса sp+10. В *a2 будет храниться сформированный адрес cputovram в ram
|
||
|
||
// Здесь был цикл на 2 полусимвола.
|
||
// Выдаются свободные адреса цепочек и формируется cputoVram1 и 2 (нам нужна одна команда вместо двух)
|
||
|
||
cputovram_cmd_from_scratch_copy:
|
||
update_free_dma3:
|
||
halfkanji_loop:
|
||
lui a1, 0x1F80
|
||
lw v0, -0x6C20(s1) // Берём адрес след свободной цепочки
|
||
ori a1, a1, 0x0348 // Берём адрес в scrathPad символа CPUtoVRAM
|
||
and v0, v0, s0 // отрезаем первый байт у свободной цепочки &00FFFFFF
|
||
or v0, v0, s7 // Прикручиваем адрес 80....... || nextChain
|
||
|
||
sw v0, 0x0(a2) // Сохранение след полного адреса в sp+0x10
|
||
|
||
lw v1, -0x6C20(s1) // Берём ЕЩЁ РАЗ адрес свободной цепочки, но уже в v1
|
||
lw v0, 0x0(v0) // Разыменование - v0 = *v0
|
||
|
||
lw a0, 0x4(s5) // Грузим счётчик
|
||
and v1, t2 // Отрезаем префикс у адреса свободной цепочки в v1 (0xFF - Мы грузили адрес свободной цепочки, чтобы взять префикс FF???)
|
||
and v0, s0 // чистим адрес от префикса (который и так 00, но неизвестно, что было в данных)(0x00FFFFFF)
|
||
or v1, v0 // в v1 - адрес NextFreeDMA cо старым префиксом
|
||
addiu a0, -0x01 // Декремент счётчика
|
||
|
||
sw v1, -0x6C20(s1) // Сохраняем NextFreeDMA
|
||
sw a0, 0x4(s5) // save counter
|
||
|
||
lw a0, 0x0(a2) // Грузим след свободный адрес из *sp+10
|
||
addu v1, a3, a1 // v1 = смещение полусимвола + начало первого символа в scratch (указание на начало данных полусимвола)
|
||
or v0, v1, a0 // v0 = Проверка на выравнивание адресов чтения и записи
|
||
andi v0, v0, 0x0003 // путём схлопывания их адресов через or и проверки первых 2 битов (то есть числа 3).
|
||
beqz v0, Copy16Bytes // Если биты пусты, значит оба адреса выровнены по 4 байта, можно копировать word целиком
|
||
|
||
addiu v0, v1, 0x40 // v0 = максимальный адрес копирования исходных данных символа в Scratch
|
||
|
||
|
||
// Копирование данных команды CPUtoVRAM символа из scratchpad в RAM
|
||
|
||
// Невыровненное копирование по 2 байта
|
||
CopyBy2Bytes:
|
||
lwl t7, 0x3(v1) // Загрузка полусловами по 2 байта
|
||
lwr t7, 0x0(v1)
|
||
lwl t8, 0x7(v1)
|
||
lwr t8, 0x4(v1)
|
||
lwl t5, 0xB(v1)
|
||
lwr t5, 0x8(v1)
|
||
lwl t6, 0xF(v1)
|
||
lwr t6, 0xC(v1)
|
||
|
||
swl t7, 0x3(a0) // Сохранение с выравниванием
|
||
swr t7, 0x0(a0)
|
||
swl t8, 0x7(a0)
|
||
swr t8, 0x4(a0)
|
||
swl t5, 0xB(a0)
|
||
swr t5, 0x8(a0)
|
||
swl t6, 0xF(a0)
|
||
swr t6, 0xC(a0)
|
||
|
||
addiu v1, 0x10 // Смещение адреса в scratch
|
||
bne v1, v0, CopyBy2Bytes // Проверяем на достижение целевого адреса
|
||
addiu a0, a0, 0x10 // Следующий целевой адрес
|
||
|
||
j last4BytesCopy
|
||
nop
|
||
|
||
// Быстрое выровненное копирование по 4 байта
|
||
Copy16Bytes:
|
||
lw t7, 0x0(v1) // Загрузка 32-битных слов
|
||
lw t8, 0x4(v1)
|
||
lw t5, 0x8(v1)
|
||
lw t6, 0xC(v1)
|
||
|
||
sw t7, 0x0(a0) // Сохранение
|
||
sw t8, 0x4(a0)
|
||
sw t5, 0x8(a0)
|
||
sw t6, 0xC(a0)
|
||
|
||
addiu v1, 0x10 // Смещение адреса в scratch
|
||
bne v1, v0, Copy16Bytes // Проверяем на достижение целевого адреса
|
||
addiu a0, a0, 0x10 // Следующий целевой адрес
|
||
|
||
// Завершение копирования
|
||
last4BytesCopy:
|
||
lwl t7, 0x3(v1) // Копирование остатка
|
||
lwr t7, 0x0(v1)
|
||
// lwl t8, 0x7(v1)
|
||
// lwr t8, 0x4(v1)
|
||
nop
|
||
swl t7, 0x3(a0)
|
||
swr t7, 0x0(a0)
|
||
// swl t8, 0x7(a0)
|
||
// swr t8, 0x4(a0)
|
||
|
||
// Здесь старый код цикла копирования второго полукандзи
|
||
// addiu a3, a3, 0x44 // Увеличение смещения
|
||
// addiu t0, t0, 0x1 // Инкремент счетчика полусимволов
|
||
// sltiu v0, t0, 0x2 // Если счётчик <2,
|
||
// bne v0, zero, halfkanji_loop// то считываем ещё раз,
|
||
// addiu a2, a2, 0x4 // увеличивая адрес хранения команды полусимвола (a2 = sp+14)
|
||
|
||
// Линковка DMA-команд
|
||
link_char_dma_cmds:
|
||
lw a0, 0x10(sp) // в a0 грузим адрес первого полусимвола (который хранился в *sp+10)
|
||
lw v1, 0x18(t1) // Получили адрес rect из print_params (вернее, видимо, адрес начала цепочки)
|
||
lw v0, 0x0(a0) // v0 = *a0 - длина команды cpoutovram (10/0d 000000) без следующего адреса
|
||
lw v1, 0x0(v1) // v1 = *v1 - разыменовали и получили длину команды rect (04ffffff) без следующего адреса
|
||
and v0, t2 // v0 & FF000000 - чистим длину команды
|
||
and v1, s0 // v1 & 00FFFFFF - отрезали маску у длины команды rect
|
||
or v0, v1 // || Совмещаем число количества команд cpoutovram и маску rect (0dffffff)
|
||
sw v0, 0x0(a0) // сохраняем длину команды cpoutovram с маской ffffff в цепочку
|
||
|
||
lw a0, 0x18(t1) // Опять грузим адрес rect из print_params
|
||
lw v1, 0x10(sp) // ОпятЬ! грузим адрес первого полусимвола
|
||
lw v0, 0x0(a0) // v0 = *a0, то есть грузим длину команды с маской (10ffffff) вместо адреса
|
||
and v1, s0 // &00ffffff у адреса первого полусимвола (режем префикс), оставляя адрес
|
||
and v0, t2 // в v0 - длина команды rect. Чистим (&ff000000)
|
||
or v0, v1 // СОЗДАЁТСЯ ЛИНК команды rect и след. адреса (04+nextCommandAddr)
|
||
sw v0, 0x0(a0) // сохраняем линк в цепочке (команда rect указывает на следующую команду)
|
||
|
||
// односимвольная система
|
||
j disabled_lower_kanji // перепрыгиваем линковку нижнего полусимвола
|
||
lw v0, 0x10(sp) // сразу грузим адрес цепочки cpoutovram1 для сохранения в print_params
|
||
|
||
|
||
enabled_lower_kanji:
|
||
// Второй полусимвол - код оставлен, чтобы понимать принцип линковки
|
||
// зачем-то сначала адрес следующей команды превращается в xxFFFFFF,
|
||
// скорее всего это маркер конца цепочки текста
|
||
lw a0, 0x14(sp) // в a0 грузим адрес второго полусимвола (который хранился в *sp+14)
|
||
lw v1, 0x10(sp) // грузим адрес первого полусимвола
|
||
lw v0, 0x0(a0) // v0 = *a0, грузим команду с маской (0d000000) вместо адреса
|
||
lw v1, 0x0(v1) // v1 = *v1, Разыменовываем адрес первого полусимвола (0dffffff)
|
||
and v0, t2 // в v0 - длина команды cpoutovram2 (&ff000000)
|
||
and v1, s0 // &00ffffff у адреса первого полусимвола (режем префикс)
|
||
or v0, v1 // получаем длину команды cpoutovram2 с &00ffffff (0dffffff)
|
||
sw v0, 0x0(a0) // сохраняем длину команды cpoutovram2 в цепочку
|
||
|
||
lw a0, 0x10(sp) // грузим адрес первого полусимвола
|
||
lw v1, 0x14(sp) // v1 - адрес второго полусимвола
|
||
lw v0, 0x0(a0) // v0 = *a0 Разыменовываем адрес первого полусимвола
|
||
and v1, s0 // &00ffffff у адреса второго полусимвола (режем префикс)
|
||
and v0, t2 // в v0 - длина команды cpoutovram1. чистим (&ff000000)
|
||
or v0, v1 // СОЗДАЁТСЯ ЛИНК команды cpoutovram1 и cpoutovram2 (0d+nextCommandAddr)
|
||
sw v0, 0x0(a0) // сохраняем линк в цепочке (команда cpoutovram1 указывает на следующую команду)
|
||
|
||
lw v0, 0x14(sp) // v0 - адрес второго полусимвола
|
||
|
||
disabled_lower_kanji:
|
||
addiu s2, 0x1 // Увеличение счетчика страниц
|
||
sw v0, 0x18(t1) // Обновление связи в print_params (она указывает на адрес последней команды символа в этой странице)
|
||
|
||
sltiu v0, s2, 0x2 // Проверка счётчика текущей страницы
|
||
bnez v0, PageLoop // Если < 2, то формируем символ ещё раз в новую страницу.
|
||
addiu t1, t1,0x4 // смещаем базовый адрес text_params для следующей страницы
|
||
|
||
// Обновление индекса и проверка конца чтения
|
||
SpaceCharJumpHere:
|
||
lhu v0, 0x4(s4) // Грузим счётчик символов в print_params
|
||
nop
|
||
addiu v0, v0, 0x1 // Инкремент счётчика
|
||
|
||
//Моя проверка переменных
|
||
lui t3, MyAddr
|
||
lhu v1, -0x10(t3) // грузим количество символов
|
||
lbu r4, 0x0(s6) // Читаем следующую букву
|
||
addiu s6, s6, 0x1 // Смещаем позицию чтения
|
||
sh v0, 0x4(s4) // Сохраняем счётчик сформированных символов в строке в print_params
|
||
|
||
bne v0, v1, NextChar // Сравниваем переменные - моё общее кол-во символов и счётчик символов из print_params. Завершаемся, если количество сошлось.
|
||
nop
|
||
|
||
//Старый код с иероглифами
|
||
// lhu a0, 0x0(s6) // Загрузка следующего символа
|
||
// nop
|
||
// sltiu v0, a0, 0x1000 // Проверка на конец текста
|
||
//addiu s6, s6, 0x2 // Старый код - следующий символ
|
||
|
||
|
||
// Закрытие отрисовки текста
|
||
TextEnd:
|
||
clear s2 // Очистка счётчика текстурных страниц s2
|
||
lui s3, 0x8008 // s3 = 80080000
|
||
addiu s5, s3, -0x6C20 // s5 = 800793e0 - nextFreeChain
|
||
lui s1, 0xFF
|
||
ori s1, s1, 0xFFFF // s1 = 00ffffff
|
||
move s0, s4 // s0 = print_params
|
||
clear a1 // Очистка a1
|
||
|
||
// Цикл установки текстурных страниц
|
||
PageTLoop:
|
||
li a2, 0x1 // Длина команды текстурных страниц. Читается в SetDrawTPage
|
||
addu s2, a2 // Обновление счетчика s += a2
|
||
lui v0, 0x8000 // Префикс адресации v0 = 80000000
|
||
lui a0, 0xFF00 // Маска адреса a0 = ff000000
|
||
|
||
//Расчищаем команду под установку текстурной страницы
|
||
lw v1, -0x6C20(s3) // v1 = nextFreeDma
|
||
lw a3, 0xA0(gp) // Загрузка a3 = 34 (текстурная страница). Читается в SetDrawTPage
|
||
and v1, s1 // Очистка префикса nextFreeDma
|
||
or v1, v0 // превращаем nextFreeDma в адрес (80000000 || nextFreeDma)
|
||
sw v1, 0x30(s0) // Сохранение адреса пустой команды для drawtpage в print_params
|
||
|
||
lw v0, -0x6C20(s3) // Ещё раз v0 = nextFreeDma
|
||
lw v1, 0x0(v1) // Разыменовывание v1 = *v1. Получаем содержимое адреса след. свободной цепочки.
|
||
and v0, a0 // оставляем префикс от nextFreeDma (99,999% это будет ff)
|
||
and v1, s1 // Чиcтка адреса в цепочке от префикса
|
||
lw a0, 0x4(s5) // Грузим DMA-счетчик
|
||
or v0, v0, v1 // Объединение префикса со след пустым адресом
|
||
sw v0, -0x6C20(s3) // Сохранение nextFreeDma
|
||
addiu a0, a0, -1 // Декремент счетчика
|
||
sw a0, 0x4(s5) // Сохранение счетчика свободных команд
|
||
|
||
lw a0, 0x30(s0) // Загрузка адреса пустой команды для drawtpage из print_params
|
||
jal SetDrawTPage // Установка DrawTPage (текстурной страницы)
|
||
addiu s0, s0, 0x4 // Смещение в print_params для следующей страницы
|
||
|
||
sltiu v0, s2, 0x2 // Проверка на завершение 2 страниц
|
||
bne v0, zero, PageTLoop // Если не закончили, продолжаем
|
||
clear a1 // Очистка a1
|
||
|
||
// Восстановление регистров и возврат
|
||
ReturnFromFunction:
|
||
move v0, s4 // Возвращаем DMA адрес
|
||
lw ra, 0x4C(sp) // Восстановление return address
|
||
lw s8, 0x48(sp) // Восстановление s8
|
||
lw s7, 0x44(sp) // Восстановление s7
|
||
lw s6, 0x40(sp) // Восстановление s6
|
||
lw s5, 0x3C(sp) // Восстановление s5
|
||
lw s4, 0x38(sp) // Восстановление s4
|
||
lw s3, 0x34(sp) // Восстановление s3
|
||
lw s2, 0x30(sp) // Восстановление s2
|
||
lw s1, 0x2C(sp) // Восстановление s1
|
||
lw s0, 0x28(sp) // Восстановление s0
|
||
|
||
jr ra // Возврат из функции
|
||
addiu sp, sp, 0x50 // Восстановление стека
|
||
|
||
|
||
.include "cd_extra_charload.asm"
|
||
.include "charCalcs.asm"
|
||
|
||
.close
|
||
.include "cd_extra_txtpatches.asm" //misc text patches
|
||
|
||
|
||
// COMPILE COMMAND: ./armips -sym cd_extra_main.map cd_extra_main.asm |