ZX Spectrum Next. Слой спрайтов.
Вступление
Здраствуйте вам!
На написание большого кол-ва букав, что я раньше ни когда не делал, меня подтолкнул Александр Шарихин (Nihirash).
Ни так давно меня заинтересовала относительно новая платформа Zx Spectrum Next от наших забугорных друзей. Заинтересовала по аппаратным возможностям, к тому-же требовались знания исключительно ассемблера Z80 с коим я знаком с детства. Мое первое знакомство с аппаратными возможностями Next пало на режим Sprite, о котором я и хочу рассказать.
Орфографическим ошибкам быть!
NEXTREG
Кратко о том как пользоваться регистрами Next.Каждый регистр выполняет отведенное ему действие.
Манипулировать требуемым регистром можно передавая регистру 1 байт, биты которого отвечают за то или иное действие.
Некоторые компиляторы понимают мнемонику `nextreg` через которую осуществляется передача значений заданному регистру.
`nextreg` может принимать байт, либо содержимое регистра `А`. Синтаксис выглядит следующим образом:
nextreg register, value
nextreg register
Создадим макросы регистров:
macro NEXTREG register,value
dw #ED91
db register, value
endm
macro NEXTREG_A register
dw #ED92
db register
endm
Используем следующим образом:
NEXTREG #07,%00000011 // 20t
//----------------или-----------------
ld a,%00000011 // 7t
NEXTREG_A #07 // 17t
… о коде выше для общего понимания.
Мы задали частоту для Next равную 28MHz. Как это работает?
В регистр #07 загружаем %00000011
Регистр #07 (TURBO CONTROL REGISTER) отвечает за герцовку Next`а в который можно установить или прочитать значения во время исполнения кода.
Таблица значений передаваемых в регистр:
Бит | Функция |
---|---|
7-2 | Резерв |
1-0 | Установка CPU speed %00 = 3.5MHz %01 = 7MHz %10 = 14MHz %11 = 28MHz (works since core 3.0) |
Прочитать значения из регистра можно если регистр читаем (Readable), и сделать это можно только через специальный порт #243B в который нужно указать требуемый регистр и следом считать из порта #253B результат.
ld a,#07
ld bc,#243B
out (c),a ; посылаем в порт необходимый регистр (Readable)
inc b ; #253B
in a,(c) ; считываем значения указанного регистра
Таблица значений полученных из регистра:
Бит | Функция |
---|---|
7-6 | Резерв |
5-4 | Текущая фактическая скорость процессора |
3-2 | Резерв |
1-0 | Запрограммированная скорость процессора |
Так-же стоит отметить что:
NEXTREG #07,%00000011
эквивалентно записи:
ld a,#07 ; нужный нам регистр
ld bc,#243B ; порт приема порта (TBBlue Register Select)
out (c),a ; посылаем в порт регистр (Writable)
inc b ; #253B порт доступа к атрибутам заданного регистра (TBBlue Register Access)
ld a,%00000011
out (c),a ; записываем значение в выбраный регистр.
Sprite
В Next можно работать с несколькими отображаемыми слоями комбинируя их по необходимости.
Существуют следующие слои: Sprite, LoRes, Layer 2, ULA, Tilemap. Некоторые из слоев имеют разные разрешения экрана и плотность бит на пиксель.
Существуют спрайты двух типов:
256 цветов — [1 пиксель = 8 бит]
16 цветов — [1 пиксель = 4 бит]
Опишу минимум работы со слоем спрайтов известный мне. Буду признателен дополнениями и поправками в коментариях.
Возьмем заготовленные спрайты шрифта 8 бит на точку (256 цветов). Каждый спрайт занимает 256 байт. Под работу со спрайтами выделена память FPGA = 16кб. В случае использования 256-цветных спрайтов мы сможем заполнить память 64-мя спрайтами.
Подгрузим спрайты и зальём их в FPGA память.
ld hl,font ; адрес спрайтов - наш шрифт
ld de,#4000 ; размер наших спрайтов котрые мы будем перегонять в FPGA
; где D дает понять какое кол-во шаблонов мы загружаем (#40) - 64
ld bc,#303b ; Порт индексов шаблона спрайта
out (c),e ; установим индекс шаблона = 0
; индекс шаблона определяет где находится спрайт в FPGA памяти для вызова и работы с ним
; переполнение FPGA памяти приведет к неожиданным последствиям
; к примеру если мы установим индекс шаблона = 1 то загрузить в FPGA мы можем не больше #3F00 (63 шаблона)
ld bc,#005b ; порт загрузки шаблонов спрайтов в память FPGA
sendByte
outinb ; аналог OUTI без декремента B
; каждые 256 байт индекс шаблона будет увеличиваться автоматически
dec de
ld a,d
or e
jr nz,sendByte
font:
incbin "font.SPR"
Использовать 16кб памяти в коде для спрайтов не лучшая идея. Можно воспользоваться системой esxDOS и подгружать в буфер по 256 байт из нашего файла 16кб с переброской в FPGA. Возможно существует способ загрузки непосредственно в FPGA, но мне пока не известно.
Приступим к работе со спрайтами отправленными в FPGA.
Что бы включить слой спрайтов нужно активировать бит 0 порта Sprite Control Register #15
; включим слой спрайтов
; регистр #15 предназначен для управления слоем спрайтов
nextreg #15,%00000001
; бит 0 регистра #15 отвечает за отображение слоя спрайтов
Давайте отобразим на экране символ шрифта, букву `A`:
ld bc,#0057 ; порт атрибутов спрайта
ld de,#0000 ; X, Y
ld l,#21|#80 ; #21 = 'A'; #80 = set bit 7
; индекс шаблона спрайта с включеным битом 7 который отвечает за видимость указанного спрайта
out (c),e ; attribute 0
out (c),d ; attribute 1
out (c),b ; attribute 2
out (c),l ; attribute 3
Коротко об атрибутах:
- attribute 0 — координата X
- attribute 1 — координата Y
- attribute 2 — палитра, зераклирование, вращение, старший бит координаты X
- attribute 3 — видимость спрайта, номер шаблона спрайта, досупность attribute 4
- attribute 4 — масштаб спрайта, тип спрайта (после сброса = 256 color), старший бит координаты Y
Запустите код что-бы увидеть…
… ничего
Область отображения слоя спрайтов центруется на экране и равна 320х256 пикселей. То есть указав координаты (0,0) мы фактически рисуем на координатах (-32,-32), получается под бордюром экрана спектрума. Таким образом слой спрайтов занимает на 32 пикселя больше со всех сторон спектрумовского экрана.
Измените координаты в коде выше:
ld de,#2020 ; X, Y
И увидите спрайт на экране так, если бы он раполагался на спекртумовском экране на координатах (0,0)
… но давайте вернемся обратно.
ld de,#0000 ; X, Y
Убрать ограничения видимости слоя спрайтов можно битом 1 (Over border) регистра управления слоем спрайтов #15
nextreg #15,%00000011 ; бит 0 = отображение слоя спрайтов
; бит 1 = отсечение границы видимости
Почему спрайт отобразился с черным фоном в пределах своих границ?
В Next прозрачным считается цвет с кодом #E3 для 256-цветного и режима и #03 для 16-цветного режима. Спрайты шрифта были созданы на черном фоне с кодом #00 что не соответсвует прозрачному цвету Next`а. Тут нам на помощь придет регистр управления прозрачностью слоя спрайтов Sprites Transparency Index Register #4B.
Укажем регистру какой цвет для спрайта использовать в качестве прозрачного:
nextreg #4B,#00
Результат будет следующим:
Задаимся вопросом как нам быть с координатами? Мы знаем что область видимости у слоя спрайтов 320х256. atribute 0 отвечающий за координату X спрайта имеет 1 байт, что не позволит отрисовать спрайт за пределами X > 255.
ld de,#00FF ; X, Y (max X = 255)
На помощь нам придет atribute 2. Немного изменим код записи атрибутов:
ld bc,#0057 ; порт атрибутов спрайта
ld de,#0030 ; X, Y (X = X + 256)
ld l,#21|#80 ; #21 = 'A'; #80 = set bit 7
; индекс шаблона спрайта с включеным битом 7 который отвечает за видимость указанного спрайта
out (c),e ; attribute 0
out (c),d ; attribute 1
ld a,1 ; = 256
out (c),a ; attribute 2
out (c),l ; attribute 3
Включение бита 0 в atribute 2 дает смещение X на 256. В данном случае мы отрисуем спрайт в левом верхнем углу экрана на координатах #100 + #30 = #130 (256 + 48 = 304)
Заполним всю видимую область по X последовательными спрайтами начиная с буквы 'A'.
Немного изменим наш код:
; запись атрибутов в порт #57 производится с автоинкрементом
; если attribute 4 не активирован то запись атрибутов следующего спрайта произойдет
; после ввода последнего 4-го атрибута, иначе после 5-го.
ld bc,#0057 ; порт атрибутов спрайта
ld de,#0000 ; X, Y
ld l,#21|#80 ; #21 = 'A'; #80 = видимость спрайта
; индекс шаблона спрайта с включеным битом 7 который отвечает за видимость указанного спрайта
ld h,#00 ; overload X
exx
ld b,#140/#10 ; кол-во спрайтов вместимых в экран слоя спрайтов
nextSprite:
exx
out (c),e ; attribute 0 | X
out (c),d ; attribute 1 | Y
ld a,h
out (c),a ; attribute 2 | bit 0 = смещение X
out (c),l ; attribute 3 | индекс спрайта, видимость
inc l ; следующий спрайт (буква)
ld a,e
add #10 ; сдвигаем Х на 16
jr nc,noOverload:
ld h,1 ; активируем смещение X на 256
noOverload:
ld e,a
exx
djnz nextSprite
Результат:
Трансформация
Возьмем один из спрайтов отображенных на экране и проделаем над ним всякое. Выберем спрайт с буквой 'D'.Для смены требуемых атрибутов конкретного спрайта нужно обратиться к регистру Sprite port-mirror Index Register #34 указав ему индекс требуемого спрайта. После выбора спрайта можно производить запись в любой из его атрибутов. Для это существуют последовательные регистры #35,#36,#37,#38,#39 соответствующие атрибутам attribute 0,1,2,3,4
Изменим шаблон спрайта с изображением буквы 'D' на шаблон с изображением буквы 'Z'
Продолжим наш код:
nextreg #34,#03 ; укажем индекс спрайта для работы с ним
; в спрайте под индексом 3 у нас записан шаблон буквы 'D'
nextreg #38,#3a|#80|#40
; attribute 3 позволяет изменить шаблон спрайта
; #3a - шаблон спрайта с изоражением буквы 'Z'
; #80 - бит 7 видимость спрайта
; #40 - бит 6 разрешит изменение attribute 4, понадобится для масштабирования
Приминимо для анимации.
Отразим этот-же спрайт по Y:
nextreg #37,%00000100 ; attribute 2 - бит 2 перевернет изображение по Y
увеличим его размер в 2 раза:
nextreg #39,%00001010 ; attribute 4
; биты 2-1 = масштабирование по оси Y
; биты 4-3 = масштабирование по оси X
; 1x (16 pixels), %01 = 2x, %10 = 4x, %11 = 8x (128 pixels)
Поменяем координату Y сразу у всех спрайтов.
nextreg #34,#00 ; #00 - индекс первого спрайта
ld a,#20 ; новая координата Y
ld b,#14 ; кол-во обрабатываемых спрайтов
nextreg #76,a ; заносим в текущий спрайт новую коориднату Y
; регистр #76 эквивалент регистра #36 (attribute 1) за исключением автоинкремента
djnz $-3
Выше мы использовали новый регистр #76. Существуют альтернативные регистры работы с атрибутами, их отличие в том что после каждой записи они будут обращаться к следующему спрайту. Это ригистры: #75,#76,#77,#78,#79, порядок атрибутов тот-же. Так-же нам было необходимо заного занести в регистр #34 нулевой индекс.
И сразу же давайте зальём стандартный спектрумовский экран чем нибудь.
ld hl,#4000
ld de,#4001
ld bc,6912
ld (hl),#07
ldir
Остальные возможностии атрибутов спрайта смотрите по соответствующим ссылкам выше.
Вернемся к регистру управления спрайтами Sprite Control Register #15
На данный момент он у нас отвечает за отображение слоя спрайтов и за отображение спрайтов по всей области 320х256 (nextreg #15,%00000011)
Изменим биты 4-2 на (%100)
nextreg #15,%00010011
Экран ULA стал поверх нашего слоя спрайтов. Биты 4-2 отвечают за приоритет отрисовки слоев. Мы установили приоритет U S L (ULA, SPRITE, LAYER 2) где первый слой находится выше второго.
Что мы можем сделать с этой ULA 'зеброй'?
Мы можем использовать регистр Global Transparency Register #14, который отвечает за прозрачность цвета слоев Layer 2, ULA и LoRes.
Глобальная прозрачность так-же как и прозрачность спрайтов имеет значение #E3 и они не пересекаются.
Зададим глобальной прозрачности новое значение '0' отвечающее за черный цвет.
nextreg #14,#00 ; установим цвет прозрачности = 0
Теперь черный цвет ULA экрана стал прозрачным и мы видим через него слой спрайтов.
И на последок рассмотрим регистры смещения ULA экрана:
ULA X Offset Register #26
ULA Y Offset Register #27
Дополним наш код:
xor a
mainLoop:
ei
halt
nextreg #26,a ; установим новую коориднату X
inc a
jr mainLoop
Экран ULA начнет движение влево. Движение касается только области экрана ULA 256х192, бордюр не затрагивается.
Это мой первый опыт написания чего-либо публично, и я устал… :)
Предложения и замечания прошу в коментарии, буду править/добавлять. (если это возможно)
Исходник
Всякое разное:
DevTools
CSpect
Config
Wiki
5 комментариев
А вот тут поподробнее бы (тем более что текст намекает что это планировалось).
Это что за покемон? Какая то недокументированная инструкция или они в Next сделали контроллер отлавливающий появление такой последовательности? Насколько я помню Z80 там оригинальный, не через FPGA, так что ему систему команд поменять не могли по идее…