В этой заметке я опишу как рисовать спрайты с помощью VDP эмулятора приставки Sega Genesis.
Процесс отрисовки спрайтов очень схож с рендерингом тайлов:
- Загрузка цветов в CRAM
- Выгрузка частей спрайтов 8×8 в VRAM
- Заполнение Sprite Table в VRAM
Для примера возьмем спрайт скелета с мечом 32×32 пикселя
Skeleton Guy [Animated] by Disthorn
CRAM
С помощью ImaGenesis сконвертируем его в цвета CRAM и паттерны VRAM для ассемблера. После этого получим два файла а формате asm, далее переписываем цвета на размер word, а тайлы нужно положить в корректном порядке для отрисовки.
Интересная информация: можно переключить автоинкремент VDP через регистр 0xF на размер word, это позволит убрать инкремент адреса из кода заливки цветов CRAM.
VRAM
В мануале сеги есть корректный порядок тайлов для больших спрайтов, но мы умнее, поэтому возьмем индексы из блога ChibiAkumas, начнем подсчет с индекса 0:
0 4 8 12
1 5 9 13
2 6 10 14
3 7 11 15
Почему все кверх ногами? А что вы хотите, ведь приставка японская! Могло быть вообще справа налево!
Поменяем вручную порядок в asm файле спрайта:
Sprite:
dc.l $11111111 ; Tile #0
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111 ; Tile #4
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111 ; Tile #8
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111111
dc.l $11111122
dc.l $11111122
dc.l $11111166
dc.l $11111166 ; Tile #12
dc.l $11111166
dc.l $11111166
и т.д.
Прогрузим спрайт как обычные тайлы/паттерны:
SpriteVRAM:
lea Sprite,a0
move.l #$40200000,vdp_control_port; write to VRAM command
move.w #128,d0 ; (16*8 rows of sprite) counter
SpriteVRAMLoop:
move.l (a0)+,vdp_data_port;
dbra d0,SpriteVRAMLoop
Для отрисовки спрайта осталось заполнить таблицу спрайтов (Sprite Table)
Sprite Table
Таблица спрайтов заполняется в VRAM, адрес ее нахождения проставляется в VDP регистре 0x05, адрес опять хитрый, посмотреть можно в мануале, пример для адреса F000:
dc.b $78 ; 0x05: Sprite table at VRAM 0xF000 (bits 0-6 = bits 9-15)
Ок, теперь запишем наш спрайт в таблицу. Для этого нужно заполнить “структуру” данных состоящую из четырех word. Бинарное описание структуры вы можете найти в мануале. Лично я сделал проще, таблицу спрайтов можно редактировать вручную в эмуляторе Exodus.
Параметры структуры очевидны из названия, например XPos, YPos – координаты, Tiles – номер стартового тайла для отрисовки, HSize, VSize – размеры спрайта путем сложения частей 8×8, HFlip, VFlip – аппаратные повороты спрайта по горизонтали и вертикали.
Очень важно помнить что спрайты могут находиться вне экрана, это корректное поведение, т.к. выгружать из памяти спрайты вне экрана – достаточно ресурсоемкое занятие.
После заполнения данных в эмуляторе, их нужно скопировать из VRAM по адресу 0xF000, Exodus также поддерживает эту возможность.
По аналогии с отрисовкой тайлов, сначала обращаемся в порт контроля VDP для начала записи по адресу 0xF000, затем в порт данных записываем структуру.
Напомню что описание адресации VRAM можно почитать в мануале, либо в блоге Nameless Algorithm.
Вкратце адресация VDP работает так:
[..DC BA98 7654 3210 …. …. …. ..FE]
Где hex это позиция бита в желаемом адресе. Первые два бита это тип запрашиваемой команды, например 01 – запись в VRAM. Тогда для адреса 0XF000 получается:
0111 0000 0000 0000 0000 0000 0000 0011 (70000003)
В итоге получаем код:
SpriteTable:
move.l #$70000003,vdp_control_port
move.w #$0100,vdp_data_port
move.w #$0F00,vdp_data_port
move.w #$0001,vdp_data_port
move.w #$0100,vdp_data_port
После этого спрайт скелета отобразится в координатах 256, 256. Круто да?
Ссылки
https://gitlab.com/demensdeum/segagenesissamples/-/tree/main/7Sprite/vasm
https://opengameart.org/content/skeleton-guy-animated
Источники
https://namelessalgorithm.com/genesis/blog/vdp/
https://www.chibiakumas.com/68000/platform3.php#LessonP27
https://plutiedev.com/sprites