////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//       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 "3_CD_EXTRA/BIN/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 "3_CD_EXTRA/cd_extra_charload.asm"
.include "1_IS/charCalcs.asm"

.close
.include "3_CD_EXTRA/cd_extra_txtpatches.asm"       //misc text patches


//                       COMPILE COMMAND: ./armips -sym 3_CD_EXTRA/BUILD_LOGS/SLPS_028.26.map -temp 3_CD_EXTRA/BUILD_LOGS/SLPS_028.26.map 3_CD_EXTRA/cd_extra_main.asm