; ===== ОСНОВНАЯ ФУНКЦИЯ ПЕЧАТИ ===== .org 0x80090000 ExternalPrint: lhu v0,0x0(a0) ; Читаем 2 байта текста nop srl v1,v0,0xD ; Проверяем 13-й бит (система 1-байтовых символов) bne v1,zero,MyPrintLineRoutine ; Если установлен - используем кастомную систему clear v1 j PrintBigDMAText ; Иначе - оригинальная система печати nop ; ===== КАСТОМНАЯ СИСТЕМА ПЕЧАТИ ===== MyPrintLineRoutine: addiu sp,sp,-0x50 ; Сохраняем контекст sw s4,0x38(sp) move s4,r4 ; s4 = адрес текста для чтения sw s0,0x28(sp) move s0,r5 ; s0 = параметр X sw s1,0x2c(sp) move s1,r6 ; s1 = параметр Y sw s3,0x34(sp) ; ===== ИНИЦИАЛИЗАЦИЯ ПАРАМЕТРОВ ТЕКСТА ===== andi v0,v0,0xff addiu s4,s4,0x2 ; Сдвигаем указатель текста на +2 байта lui s3,MyAddr sw v0,-0x10(s3) ; Сохраняем счётчик байтов @ 8008fff0 sh zero,-0xE(s3) ; Очищаем счётчик символов @ 8008fff2 ; ===== ИНИЦИАЛИЗАЦИЯ DMA ЦЕПОЧЕК ===== lui s3,0x1f80 ori s3,s3,0x3d0 ; s3 = 1f8003d0 - команда копирования спрайта в scratch lui t0,0xff ori t0,t0,0xffff ; t0 = 00FFFFFF (маска) andi r4,r7,0xff ; Режим текста (цвет и тень) lui r7, 0x8008 ; r7 = 80080000 lui r6, 0xff00 ; r6 = FF000000 (маска) ; ЗАГРУЗКА ЦЕПОЧКИ ФОРМИРОВАНИЯ СИМВОЛОВ addiu t1,r7,-0x31a8 ; t1 = адрес цепочки формирования (8007ce58) lw v1, -0x31a8(r7) ; v1 = текущая свободная цепочка формирования lbu r5,0x60(sp) ; Флаг тени из стека and r6,v1,r6 ; r6 = старшие байты цепочки (FFxxxxxx) andi r5,r5,0xff ; Очищаем режим текста and v1,v1,t0 ; v1 = младшие байты цепочки (00xxxxxx) or s5,v1,v0 ; s5 = адрес текущей цепочки формирования (80xxxxxx) ; ОБНОВЛЕНИЕ СЧЁТЧИКА И ЦЕПОЧКИ lw v0,0x4(t1) ; Счётчик команд DMA lw v1,0x0(s5) ; Следующая свободная цепочка addiu v0,v0,-0x1 ; Уменьшаем счётчик and v1,v1,t0 ; Обрезаем старший байт or r6,r6,v1 ; Объединяем с старшими байтами sw v0,0x4(t1) ; Сохраняем счётчик jal storeColor sw r6,-0x31a8(r7) ; Сохраняем обновлённую цепочку ; ===== ИНИЦИАЛИЗАЦИЯ ЦЕПОЧКИ КОПИРОВАНИЯ ===== move r4,s5 ; Адрес текущей цепочки sll r5,s0,0x10 ; Координата X sra r5,r5,0x10 sll r6,s1,0x10 ; Координата Y jal initCopyCharChain ; Инициализация цепочки копирования sra r6,r6,0x10 ; ===== НАСТРОЙКА SCRATCH PAD ДЛЯ DMA ===== lui r6,0x1f80 ori r6,r6,0x348 ; r6 = 1f800348 - начало данных символа в scratch ; ... (паттерны 4-bit и инициализация пропущены для краткости) ; ===== ОСНОВНОЙ ЦИКЛ ОБРАБОТКИ СИМВОЛОВ ===== NextChar: addiu r4,r4,-0x20 ; Сдвигаем непечатаемые символы ; ОБРАБОТКА ПРОБЕЛОВ bne r4,zero,SpaceCheckBranch nop lui t2,MyAddr lh v0,-0xE(t2) ; Текущий счётчик символов nop lh t3,-0x10(t2) ; Общий счётчик символов addiu v0,v0,1 beq v0,t3,SpaceCheckBranch ; Если последний символ - не обрабатываем пробел nop ; ПРОБЕЛ - только сдвигаем позицию sh v0,-0xE(t2) ; Обновляем счётчик nop lbu r4,0x0(s4) ; Следующий символ ; СДВИГ ПОЗИЦИИ X lhu v0,0xa8(gp) ; Начальный X lhu v1,0x8(s3) ; Текущий X в scratch addiu v0,v0,0x6 ; Сдвиг на 6 пикселей addu v1,v1,v0 ; Новый X sh v1,0x8(s3) ; Обновляем в scratch DMA j NextChar addiu s4,s4,0x1 ; Следующий байт текста ; ===== ОБРАБОТКА НЕ-ПРОБЕЛЬНЫХ СИМВОЛОВ ===== SpaceCheckBranch: jal makeCharPixelsCustom ; СОЗДАЁМ ПИКСЕЛИ СИМВОЛА В SCRATCH clear s2 ; ===== ФОРМИРОВАНИЕ КОМАНДЫ КОПИРОВАНИЯ SPRITE ===== PageLoop: ; ЗАГРУЗКА ЦЕПОЧКИ КОПИРОВАНИЯ SPRITE lw v0,-0x3198(s6) ; Адрес свободной цепочки копирования (8007ce68) nop and r4,v0,t2 ; Старшие байты and v0,v0,s0 ; Младшие байты or r5,v0,s1 ; r5 = полный адрес цепочки (80xxxxxx) ; ОБНОВЛЕНИЕ СЧЁТЧИКА КОПИРОВАНИЯ lw v0,0x4(s7) ; Счётчик команд копирования lw v1,0x0(r5) ; Следующая свободная цепочка addiu v0,v0,-0x1 ; Уменьшаем счётчик and v1,v1,s0 ; Обрезаем старший байт or r4,r4,v1 ; Объединяем sw v0,0x4(s7) ; Сохраняем счётчик sw r4,-0x3198(s6) ; Сохраняем обновлённую цепочку ; ===== КОПИРОВАНИЕ ИЗ SCRATCH В ЦЕПОЧКУ ===== CopyCmdFromScratch: ; Копируем команду DMA из scratch в цепочку lw t9,0x0(s3) ; Количество команд (04000000) lw t7,0x4(s3) ; Команда копирования спрайта (64808080) lw t8,0x8(s3) ; Координаты XY на экране lw t5,0xc(s3) ; CLUT и координаты текстуры ; СОХРАНЯЕМ В ЦЕПОЧКУ КОПИРОВАНИЯ sw t9,0x0(r5) ; sw t7,0x4(r5) sw t8,0x8(r5) sw t5,0xc(r5) lw t9,0x10(s3) ; Размер спрайта после копирования nop sw t9,0x10(r5) ; Последняя команда ; ===== СВЯЗЫВАНИЕ ЦЕПОЧЕК ===== ; СВЯЗЬ: ЦЕПОЧКА ФОРМИРОВАНИЯ → ЦЕПОЧКА КОПИРОВАНИЯ lw v1,0x18(t1) ; Текущая цепочка формирования lw v0,0x0(r5) ; Текущая команда копирования lw v1,0x0(v1) ; Следующая цепочка формирования and v0,v0,t2 ; Маска старших байтов and v1,v1,s0 ; Маска младших байтов or v0,v0,v1 ; Объединяем sw v0,0x0(r5) ; Обновляем команду ; ОБНОВЛЯЕМ ССЫЛКУ В ЦЕПОЧКЕ ФОРМИРОВАНИЯ lw r4,0x18(t1) ; Адрес цепочки формирования nop lw v0,0x0(r4) ; Текущая команда формирования and v1,r5,s0 ; Адрес цепочки копирования and v0,v0,t2 ; Маска старших байтов or v0,v0,v1 ; Объединяем sw v0,0x0(r4) ; Сохраняем обновлённую ссылку sw r5,0x18(t1) ; Сохраняем текущую цепочку копирования ; ===== ПРОВЕРКА ПЕРВОГО СИМВОЛА ===== lhu v0,0x4(t6) ; Счётчик символов nop bne v0,zero,NotFirstChar ; Если не первый символ - пропускаем nop ; ИНИЦИАЛИЗАЦИЯ ДЛЯ ПЕРВОГО СИМВОЛА sw r5,0x28(t1) ; Устанавливаем первую цепочку символа sw t8,0xc(t6) ; Сохраняем координаты в параметрах NotFirstChar: ; ===== КОПИРОВАНИЕ ДАННЫХ СИМВОЛА ИЗ SCRATCH ===== lui r5,0x1f80 ori r5,r5,0x348 ; r5 = данные символа в scratch lw v0,-0x31a8(t3) ; Следующая свободная цепочка формирования and r4,v0,t2 ; Старшие байты and v0,v0,s0 ; Младшие байты or r7,v0,s1 ; r7 = адрес цепочки (80xxxxxx) move r6,r7 ; r6 = адрес назначения ; ... (копирование данных пропущено для краткости) ; ===== СВЯЗЫВАНИЕ: ДАННЫЕ СИМВОЛА → КОМАНДА КОПИРОВАНИЯ ===== lw v1,0x18(t1) ; Текущая команда копирования lw v0,0x0(r7) ; Текущие данные символа lw v1,0x0(v1) ; Следующая команда копирования and v0,v0,t2 ; Маска старших байтов and v1,v1,s0 ; Маска младших байтов or v0,v0,v1 ; Объединяем sw v0,0x0(r7) ; Обновляем данные символа ; ОБНОВЛЯЕМ ССЫЛКУ В КОМАНДЕ КОПИРОВАНИЯ lw r4,0x18(t1) ; Адрес команды копирования nop lw v0,0x0(r4) ; Текущая команда and v1,r7,s0 ; Адрес данных символа and v0,v0,t2 ; Маска старших байтов or v0,v0,v1 ; Объединяем sw v0,0x0(r4) ; Сохраняем обновлённую ссылку sw r7,0x18(t1) ; Сохраняем текущие данные символа ; ===== СДВИГ ПОЗИЦИИ ДЛЯ СЛЕДУЮЩЕГО СИМВОЛА ===== lhu v0,0xa8(gp) ; Начальный X lhu v1,0x8(s3) ; Текущий X addiu v0,v0,0x6 ; Сдвиг на 6 пикселей addu v1,v1,v0 ; Новый X sh v1,0x8(s3) ; Обновляем ; ===== ПРОВЕРКА ЗАВЕРШЕНИЯ ТЕКСТА ===== lhu v0,0x4(s5) ; Счётчик символов DMA lui t9,MyAddr addiu v0,v0,0x1 sh v0,0x4(s5) ; Сохраняем ; ПРОВЕРКА ПЕРЕПОЛНЕНИЯ DMA lw v0,0x4(s7) ; Текущий счётчик move v1,5 ; Минимальный лимит sltu v0,v0,v1 ; Проверяем переполнение bne v0,zero,TextEnd ; Если переполнение - завершаем ; ПРОВЕРКА КОНЦА СТРОКИ lui t3,MyAddr lhu v0,-0xE(t3) ; Текущий символ lhu v1,-0x10(t3) ; Общее количество символов addiu v0,v0,0x1 lbu r4,0x0(s4) ; Следующий символ sh v0,-0xE(t3) ; Сохраняем счётчик addiu s4,s4,0x1 ; Сдвигаем указатель bne v0,v1,NextChar ; Если не конец - продолжаем nop ; ===== ЗАВЕРШЕНИЕ ОБРАБОТКИ ТЕКСТА ===== TextEnd: ; ... (восстановление контекста и завершение)