GBA ASM - День 5: Помещение спрайта на экран — Архив WASM.RU

Все статьи

GBA ASM - День 5: Помещение спрайта на экран — Архив WASM.RU

Введение

Этот день посвящён спрайтам, а если более точно тому, как вывести его на экран. Да, именно так - всё, что мы будем делать сегодня - это выведем на экран 16-ти цветный спрайт размером 32x32 на экран.

Конвертирование изображения

Так же, как и когда мы помещали на экран изображение, нам необходимо сконвертировать его во что-то понимаемое Goldroad'ом. В этот раз мы будем использовать GIFs2SPRITEs.exe, который может конвертировать GIF'ы в C-код. Теперь посмотрим, как Goldroad будет обрабатывать C-файл. Давай поиграем в короткий ответ/длинный ответ! Короткий ответ: он не умеет этого делать. Длинный ответ: мне пришлось сделать программу на VB, конвертирующую вывод GIFs2SPRITEs.exe в Goldroad'овские @DCW-выражения. Вы можете скачать GIFs2SPRITEs.exe здесь (он заархивирован). Теперь моя программа: CtoASM.exe.

Теперь нарисуйте в MSPaint'е (лучше в Photoshop'е - прим.пер.) картинку размером 32x32, сохраните её как GIF-файл под названием sprit.gif в Goldroad'овской папке "tools". Теперь откройте DOS-подсказку и делайте следующее:

> cd Полный путь к папке, где находятся sprit.gif и gifs2sprites.exe

> gifs2sprites 16 sprit.h sprit.gif

> ctoasm

Обратите внимание, что это Windows-программа. Выберите sprit.h, в диалоге сохранения файла напишите "sprit.asm". Затем нажмите на кнопку GO и нажмите на ОК, когда появится предпуреждение, что весь процесс может занять определённое количество времени.

Теперь, я надеюсь, у вас есть файл sprit.asm. Я знаю, что моя программа не эффективна, но тем не менее она работает.

OAM (Object Attribute Memory)

Если вы программировали под GBA на C раньше, то вы, возможно, знаете, что OAM - это где вы сообщаете GBA всё о вашем спрайте, например: размер, форма, цвета (16/256), X-позиция, Y-позиция, номер тайла (tile)/чара (char). OAM находится по адресу 0x7000000. OAM состоит из 128 элементов (0-127), т.е. GBA может отслеживать 128 спрайтов в OAM.

Помните, что вы также ограничены доступной памятью для изображений спрайтов. Каждый элемент OAM 8 байтов размеров, где 4 (0-3) аттрибута по два байта. Аттрибуты следующие:

*ПРИМЕЧАНИЕ: перечислены только важные части. Идите на The Pern Project за полным перечнем.

Аттрибут 0 :
Форма      |  Глубина цвета  | Y-позиция
Квадратный |   COLOR_16      | (позиция на экране по вертикали)
Высокий    |   COLOR_32      |      
Широкий    |   

Аттрибут 1 :
Size      |  X-позиция
Size_8    | (позиция на экране по горизонтали)
Size_16   |
Size_32   |

Аттрибут 2 :
Номер тайла размером 8x8 в памяти для спрайтов, с которого начинается данный
спрайт.

Аттрибут 3 :
Я думаю, что #3 предназначен для чего-то, связанного со вращением спрайтов.
Поэтому здесь о нём не будет рассказываться.

Давайте начнём писать код! Скачайте sprites.h.

;;-- Начало кода --;;

@include screen.h
@include sprites.h
b start              ; перепрыгиваем через данные
@include sprit.asm   ; это наша сконвертированная картинка
start:               ; я начал помещать двоеточия, когда понял, что это 
                     ; лучше чем так, как это позволяет делать Goldroad
Init_Sprites         ; макрос в sprites.h, инициализирующий все x-позиции спрайтов
                     ; в значение 300 

ldr r1,=REG_DISPCNT  ; делаем r1 указателем на регистр контроля экрана
ldr r2,=(MODE_0|BG2_ENABLE|OBJ_ENABLE|OBJ_MAP_1D)
str r2,[r1]          ; устанавливаем режим экрана 0 с бэкграундом 2,
                     ; включёнными спрайтами (OBJs) и мэппингом спрайтов 1d 
                     ; (чтобы это ни было - прим. пер.) (кто захочет делать 
                     ; вложенные циклы на ассемблере, если мы можем этого 
                     ; избежать!)

;;-- Хватит копировать --!!

Я собираюсь объяснить эту часть, затем я покажу остальной код. Если вы думаете, что комментарии сами по себе всё хорошо объясняют, то всё равно прочитайте это :). Во-первых, мы подключаем все необходимые файлы, потом вызываем макрос (который я великодушно накодил для вас), чтобы инициализировать спрайты. Далее мы помещаем экран в режим 0, что вы можете найти довольно необычным, так как большинство программерских туториалов, которые я видел, используют режим 3 или 4. Я использовал режим 0, потому что это тайл-режим (tiled mode), а битмэп-режимы (3 или 4) разделят память под спрайты и используют половину для экрана или буфера.

Одновременнос с установлением режима 0 мы разрешаем использование спрайтов и бэкграунда. Также мы устанавливаем загрузку спрайтов в 1D. Если бы мы поставили 2D, то нам бы пришлось отслеживать x и y. Следующая часть кода:

;;-- Код продолжается --!!

ldr r0,=OAM        ; делаем r0 указателем на OAM (0x7000000)
ldr r1,=(SQUARE|COLOR_16|10)|((SIZE_32|10)<<16)             
                   ; выше мы помещаем все аттрибуты 0 и 1 в r1, ниже будет 
                   ; подробное объяснение
str r1,[r0]+4!     ; помещаем аттрибуты 0 и 1 в OAM и увеличиваем значение 
                   ; указателя в OAM так, чтобы он указывал на аттрибут 2
ldr r1,=0          ; аттрибут 2 - это номер тайла, а наш спрайт начинается с первого: 0
str r1,[r0]        ; помещаем его в OAM. Если вы  хотели другой спрайт, вам 
                   ; нужно было сделать "+4!"
                   ; to get the OAM pointer (r0) past Attribute 2 and past rotation stuff in Attribute 3 and to
                   ; the start of the next sprite's OAM entry
                   ; чтобы получить указатель OAM (r0), пропустите аттрибут 2,
                   ; то, что относится к вращению и перейдите к началу 
                   ; следующего элемента OAM

;;-- Заканчивайте копировать здесь --!!

Теперь нужно сделать некоторое объяснение, в основном того, что касается второй строки:

ldr r1,=(SQUARE|COLOR_16|10)|((SIZE_32|10)<<16)

Обратите внимание, что так как каждый аттрибут длиной 2 байта, мы можем заполнить 32-х битный регистр двумя аттрибутами. Поэтому первые 3 вещи сORенные друг с другом - это аттрибут 0, а вторые две - это аттрибут 1. Аттрибут 1 сдвигается налево, чтобы быть в первых 16-ти битах регистра. Я понимаю, что внешне это выглядит наоборот, потому что кажется, что код помещает аттрибут 1 перед 0. Сначала я думал, что первые три сORенные вещи должны быть в первых 2-х байтах, это не заработало, поэтому я изменил код так, каким он является сейчас, и он работает.

"не чините то, что работает"

Палитра (цвета спрайтов)

Есть две палитры, одна для бэкграундов и одна для спрайтов (0x500200). Палитра - это куда помещаются цвета спрайтов. Также я должен сказать, что в C вы би использовали цикл FOR, который бы пробегал 256 раз, используя 16-ти битный указатель. В ассемблере все указатели ВСЕГДА 32-х битны, поэтому мы загружаем двойное количество данных за раз, обращаясь к памяти в два раза меньше (128 раз). Вот код, чтобы загрузить палитру из нашего сконвертированного GIF-файла.

;;-- Код продолжается --!!

ldr r1,=OBJPAL     ; r1 указывает на палитру спрайтов (0x500200)
ldr r2,=128        ; 128 - число раз, которое мы обращаемся к палитре
ldr r3,=pallete    ; палитра задана sprit.asm, который мы получили после 
                   ; конвертирования

palloop:        ; этот цикл - типичный пример кода загрузки
ldr r7,[r3]+4!  ; он помещает 4 байта из палитры в r7, а потом
str r7,[r1]+4!  ; сохраняет 4 байт в палитру спрайтов. Каждый раз используется
                ; "обратная запись" каждого регистра на 4
subs r2,r2,1    ; эта строка вычитает из количества обращений к палитре 1
bne palloop     ; и делает так, чтобы флаги представляли состояние РАВНО

; if r2 = 0   (r2==0, это для любителей C). Это BNE-выражение выполнится если
; состояние флагов НЕ РАВНО, поэтому выполнение цикла остановится, когда мы
; закончим

;;-- Заканчивайте копировать здесь --!!

Я думаю, что в этот раз комментарии к коду действительно выполняют свое предназначение, поэтому переходим к следующей секции.

CHARMEM (Sprite Picture Memory)

SPM начинается с 0x6010000. Если бы мы были в битмапном режиме экрана (3 или 4), тогда бы нам пришлось начать с 512 тайла вместо 0. Вот почему мы в режиме 0.

;;-- Код продолжается --!!

ldr r1,=CHARMEM   ; смотрите описание ниже
ldr r2,=128
ldr r3,=obj0

sprloop:
ldr r7,[r3]+4!
str r7,[r1]+4!
subs r2,r2,1
bne sprloop

;;-- Заканчивайте копировать здесь --!!

Это тот же загружающий код, изменились только указатели (спрайт в CHARMEM), а obj0 - это наш сконвертированный спрайт, находящийся в sprit.asm.

Конец кода и этого дня

Конец кода делает бесконечный цикл и это всё.

;;-- CODE CONTINUE --!!

infiniteloop
b infiniteloop

;;-- END OF CODE --!!

"Кому-то нужны объятия..."
- Ryan Stiles, "Whose line is it anyway?"

2002-2013 (c) wasm.ru