Files
Persona2-PSX-asm-sources/cd_extra_main.asm
sShemet 8e506a6912 init
2025-10-04 11:53:04 +05:00

620 lines
37 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 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