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Запрограммированная скорость процессора
Биты 5-6 содержат значение текущей скорости процессора.



Так-же стоит отметить что:

        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 комментариев

avatar
Понеслась…
  • tsl
  • +1
avatar
dw #ED91
db register, value

А вот тут поподробнее бы (тем более что текст намекает что это планировалось).
Это что за покемон? Какая то недокументированная инструкция или они в Next сделали контроллер отлавливающий появление такой последовательности? Насколько я помню Z80 там оригинальный, не через FPGA, так что ему систему команд поменять не могли по идее…
avatar
В нексте расширенная система команд используется и проц собран в ПЛИС.
avatar
Ааа, вон оно что. Забавно. Надо ознакомиться…
avatar
Лайфхак автору: Прочти ещё хотя-бы раз статью, после размещения.
  • VBI
  • 0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.