Полноэкранная графика на NES
by Shiru 09'15 mailto:shiru at mail dot ru
Пользователи ZX Spectrum и других домашних компьютеров 80-х привыкли к красочным полноэкранным заставкам, показываемым во время загрузки игры. Сама возможность отображения таких заставок им справедливо кажется естественной. И казалось бы, такая довольно мощная для своего времени игровая система, как NES, должна с лёгкостью с этим справляться – ведь показывает же она во время игры прокрутку полного экрана с крупными спрайтами со скоростью 50/60 кадров в секунду. Однако, если задуматься, можно вспомнить, что полноэкранные заставочные картинки едва ли встречались в играх, а по большей части это был чёрный экран с относительно небольшим по площади изображением. Конечно же это не случайно. Углубимся в тему.
Как вам наверняка известно, изображение слоя фона на NES строится из блоков 8x8 пикселей (тайлов), память графики которых находится в отдельном адресном пространстве видеоконтроллера, с отдельными от основного процессора шинами адреса и данных, заведёнными на разъём картриджа. В один момент времени видеоконтроллер может адресовать всего 8 килобайт графической памяти плюс 4 килобайта тайловых карт и палитру. Это даёт два набора по 256 тайлов (один тайл 16 байт, два бита на точку). Каждый из наборов может быть назначен для отображения графики спрайтов или фона. Спрайты могут использовать и оба набора одновременно (режим 8x16), но фон – всегда только один набор. Таким образом, если не применять трюков, можно отобразить картинку, состоящую только из 256 разных тайлов. Экран имеет размер 32x30 – 960 тайлов, значит 256-тайловый набор покрывает меньше трети экрана.
Также присутствуют ограничения по цвету – похожие на атрибуты знакомест на ZX Spectrum. Местные атрибуты тоже задают цвет графики в пределах своего блока, только размер блока 16x16 пикселей, а графика двухбитная, но трёхцветная – нулевое значение 'прозрачное'. Атрибуты позволяют назначить блоку одну из четырёх трёхцветных палитр, плюс общий для всего экрана цвет фона. Таким образом, для слоя фона доступно 13 цветов одновременно, плюс отдельные 12 цветов для спрайтов.
Двухбитная картинка, раскраска атрибутами, инверсия атрибутов для их лучшей заметности в примере, набор тайлов.
Понятно, что во многих играх предпринимались попытки как-то обойти ограничения. Игр, честно укладывающих титульный экран только в один набор графики фона без спрайтов, не так уж много. В основном это игры 'первой волны', не имеющие маппера и соответственно лишней памяти для детализированной графики. Либо наоборот, поздние игры с большим объёмом (упакованной) игровой графики. Для того, чтобы уместиться в 256 тайлов, изображения в заставках таких игр имеют небольшой размер и активно используют повторяющиеся тайлы, включая одноцветную заливку фона, обычно чёрную.
Типичный пример титульного экрана ранней игры без маппера. Здесь логотип занимает около половины одного из двух наборов графики.
Пара примеров, обходящихся относительно небольшим логотипом с чёрным фоном. Логотип занимает один набор почти целиком.
Активное использование повторяющихся тайлов, обратите внимание на прорисовку названия. Спрайтом выводится только движущийся вертолёт.
Попытки сделать более эффектную и визуально крупную графику за счёт использования заливок одноцветными тайлами.
В очень многих играх используется подход 'один набор графики фона плюс спрайты'. В режиме спрайтов 8x8 можно отобразить дополнительные 64 тайла из другого набора, а в режиме 8x16 – даже 128 тайлов. Однако, при этом надо учитывать ограничение в восемь спрайтов на строку растра. Поэтому спрайтами много деталей не добавить. Зато их использование позволяет уменьшить цветовые ограничения – как обойти атрибутную сетку, так и увеличить количество отображаемых цветов, задействуя палитры спрайтов. Чаще всего спрайты на заставках применялись именно для этого, иногда их графика находится в том же самом 256-тайловом наборе, что и графика фона.
Графика фона и спрайтов в одном наборе. Спрайты используются для улучшения цветовой детализации.
Графика фона и спрайтов также поместилась в один набор. Спрайты используются для обхода ограничений по цвету, ими выводится лицо, эмблема и пояс героя.
Классический экран почти весь уместился в один набор тайлов фона, но волосы героев и майка одного из них отображаются спрайтами из второго набора.
Детальный логотип с однотонными заливками на фоне занимает целый набор тайлов, герой выводится спрайтами из отдельного набора.
Почему более серьёзные попытки обойти ограничения были редки? На мой взгляд, основная причина в рентабельности. Настоящая полноэкранная картинка, в которой нет повторяющихся тайлов, займёт почти 16 килобайт, или почти четыре набора по 256 тайлов. А один набор – это, например, ещё один уникальный набор тайлов фона для уровня игры, а то и нескольких. Свободное место в играх оставалось очень редко (редкие исключения – Alien 3, Robocop 3), так как изначально выбирался минимально достаточный объём памяти, и его старались экономить, например, используя одну и ту же графику с разными палитрами на разных уровнях.
Можно предположить, что хорошее сжатие данных позволило бы обойти ограничения по памяти. Но на NES для того, чтобы графику можно было упаковать, необходимо использовать в качестве видеопамяти на картридже ОЗУ вместо ПЗУ, и хранить графику в ПЗУ кода. Само по себе это встречалось часто (Contra, Battletoads), но объём внешнего видео-ОЗУ был ограничен 8 килобайтами, что даёт те же 512 тайлов одновременно. Переслать в видеопамять дополнительные тайлы во время отображения кадра просто не получится – заранее распаковать их некуда, так как объём основного ОЗУ всего два килобайта, скорости процессора для распаковки на лету никак не хватит, к тому же нет доступа к видеопамяти во время отображения растра.
Обойти ограничение на количество тайлов фона и отобразить больше 256 тайлов, включая действительно полноэкранную картинку, конечно можно. Для этого нужно использовать маппер и ПЗУ графики, а также программный трюк – в нужный момент прохода луча по растру средствами видеоконтроллера и/или маппера переключая набор тайлов.
Такие трюки типичны для NES, они нередко используются и во время игрового процесса, например, чтобы отобразить фон уровня и панель статуса. Главная сложность в том, чтобы дождаться нужного момента. Это может быть сделано точно рассчитанной фиксированной задержкой, ожиданием в цикле установки флага 'нулевого спрайта' – когда луч доходит до спрайта с индексом 0, непрозрачные пиксели которого накладываются на непрозрачные пиксели фона – или превышения лимита спрайтов на строку, либо использованием строчного прерывания. Первые два варианта тратят время процессора впустую, третий требует наличия генератора прерываний в маппере, который встречался только в наиболее продвинутых ASIC-мапперах (MMC3, FME-7), что опять поднимает вопрос рентабельности.
При необходимости нескольких переключений за кадр только на ожидание флагов спрайтов рассчитывать нельзя, они срабатывают всего один раз за кадр. Остаются прерывания или фиксированная задержка. В последнем случае нужно дополнительно учитывать использование канала DPCM – при его работе чтение сэмплов тормозит процессор, сбивая фиксированные тайминги, что может привести к нестабильному артефакту в месте смены наборов. Способы обойти эту проблему – делать в месте переключения банков несколько одноцветных строк, либо делать место переключения банков 'внахлёст', повторяя нижнюю строку тайлов одного набора в верхней строке другого.
Рассмотрим несколько примеров подобных трюков. К сожалению, все известные мне игры используют один и тот же подход – всего два набора тайлов фона с однократным переключением между ними во время отображения кадра по флагу нулевого спрайта, с отображением элементов картинки спрайтами для обхода цветовых ограничений. Отличаются только некоторые детали реализации. Полноценных полноэкранных картинок в коммерческих играх я не встречал.
Wrestlemania Challenge. Игра использует маппер UNROM с ОЗУ графики, поэтому графика фона и спрайтов помещается в двух наборах. Нулевой спрайт представляет собой полоску из двух пикселей в нижней части буквы G в слове Challenge.
Полная картинка.
Первый и второй наборы тайлов фона.
Слой спрайтов и нулевой спрайт на нём, обведённый зелёным прямоугольником.
Battletoads and Double Dragon того же разработчика использует аналогичный принцип, но на этот раз маппер ANROM, схожий с UNROM. На титульном экране играет музыка с оцифрованными ударными высокого качества, воспроизводимыми силами процессора, а не DPCM, что несколько усложняет код, но не меняет принцип. Нулевой спрайт представляет собой одиночный пиксель с координатами 201,86.
Полная картинка.
Два набора тайлов фона и слой спрайтов.
Robocop 3 использует маппер MMC1 и ПЗУ спрайтов. Графикой фона заполнено два банка (512 тайлов). Часть девочки, лицо героя и пункты меню выводятся спрайтами, находящимися в отдельном, третьем банке. Нулевой спрайт – полоска в середине изображения девочки.
Полная картинка.
Первый и второй наборы тайлов фона.
Слой спрайтов и нулевой спрайт на нём, обведённый зелёным прямоугольником.
В качестве бонуса – карты использования памяти для этих трёх игр. Они демонстрируют, что обычно в играх на NES едва ли найдётся лишний байт, хотя бывают и исключения.
Отдельно надо сказать о трюке, часто встречающемся на других платформах – обновлении палитры во время отображения кадра, что даёт возможность отобразить больше цветов на экране, чем позволяет размер палитры. К сожалению, видеосистема NES устроена таким образом, что изменить содержимое палитры при включённом отображении без видимых артефактов очень сложно – адрес записи в видеопамять также используется как один из счётчиков для схемы отображения. Нужно успеть обновить палитру за время горизонтального гашения, попадание в которое осложняется джиттером и работой DPCM, после чего восстановить текущую позицию отображения. Времени хватит на изменение всего одной-двух ячееек палитры, плюс для скрытия артефакта придётся выключить отображение на время всей последующей строки. Поэтому трюк редко встречался в коммерческих играх, при этом области с разными палитрами разделены одной-двумя пустыми строками.
Стоит упомянуть и об альтернативе программным трюкам. Архитектура NES позволяет расширять возможности системы за счёт дополнительного железа на картридже, и она достаточно открыта, чтобы обойти некоторые ограничения трюками аппаратными, используя довольно сложные по устройству мапперы. Сама Nintendo делала это дважды.
Маппер MMC2/MMC4 (две вариации одного маппера) был специально разработан для обхода ограничения на количество тайлов фона. Он позволяет отображать до 512 тайлов, переключаясь между двумя наборами в любом месте тайловой карты, используя два тайла в качестве кода переключения набора. Использовался он всего в 4 играх, причём применение было совершенно не впечатляющим – для отображения текста и портретов персонажей одновременно с картой в трёх стратегических играх, а также портретов противников и некоторых игровых элементов в Punch-Out.
Переключение банков используется только для отображения портрета персонажа справа.
Переключение банков используется для портрета персонажа и текста в окне.
MMC5 – самый мощный из всех существующих мапперов, значительно расширяющий графические и звуковые возможности NES. Позволяет отображать до 16384 тайлов на экране, одновременно уменьшая размер атрибута до 8x8 (своя палитра для каждого тайла). Использовался в полутора десятках игр, но его потенциал не был раскрыт нигде, в частности для вывода полноэкранных картинок он не применялся.
Несмотря на потенциал маппера, графика мало отличается от других игр.
Мы рассмотрели историю. Сейчас, когда разработкой занимаются только энтузиасты, дела обстоят иначе. Вопрос рентабельности больше не стоит, а ПЗУ небольшого объёма найти стало даже труднее, чем большого. При желании можно использовать даже десятки мегабайт, такой маппер реально существует – очень похожий на UNROM, но защёлкивающий шину адреса вместо шины данных. Программируемая логика открывает возможность создания новых сложных мапперов, сравнимых с MMC5 или превосходящих его. Всё это позволяет использовать ранее малодоступные способы обхода ограничений.
Была реализована очевидная возможность вывода полноэкранных картинок трёхкратным переключением наборов тайлов во время прохода луча по растру. Делались эксперименты и с переключением между двумя экранами аналогично Gigascreen на ZX Spectrum, что может рассматриваться либо как повышение цветового разрешения на точку, либо как увеличение вертикального разрешения. Всё, что для этого нужно – большое ПЗУ графики либо 16 килобайт ОЗУ графики с поддержкой переключения банков.
Было реализовано повышение цветового разрешения за счёт уменьшения высоты атрибутов – аналогично мультиколору на ZX Spectrum. Это делается использованием двух копий тайловой карты, отличающихся областью атрибутов. При отображении кадра каждые 8 строк происходит переключение между тайловыми картами, таким образом вертикальное цветовое разрешение увеличивается вдвое, уменьшая размер атрибута до 16x8.
Примеры достижимого этими способами уровня графики можно увидеть с помощью NES Image Converter 2, позволяющего конвертировать изображения в форматы 256x240 (без мерцания), 256x480 или 512x240 (с мерцанием), с атрибутами 16x8.
Конечно, на практике подобные программные способы обхода ограничений в основном пригодны для статичных изображений и мало востребованы в играх.
Аппаратный подход также открывает новые возможности. При применении дополнительного ОЗУ для тайловых карт, которое крайне редко встречалось в коммерческих играх (4 игры), можно получить дополнительные две тайловые карты, а значит, увеличить цветовое разрешение предыдущим методом в четыре раза, получив атрибуты 16x4. Также возможно создать маппер, сочетающий возможности MMC5 и дополнительное ОЗУ тайловых карт, что позволит уменьшить атрибуты до 8x4. С ещё более сложным маппером можно достичь предельного цветового разрешения 8x1. Пока подобные расширения реализованы не были.
В заключении пара слов о 'неспектрумах'. В статье рассмотрены только способы обхода графических ограничений, преполагающие, что железо самой приставки не подвергается никаким изменениям. Но существуют и примеры обратного подхода – расширение возможностей самой приставки. Например, в середине 2006-х это было сделано китайскими инженерами, разработавшими набор чипов VT03, на котором построено небольшое количество клонов тех лет. В них, помимо прочего, доступны режимы расширенной графики с 16-цветными тайлами и спрайтами. До сих пор доступен сайт разработчика, где можно найти документацию на английском языке и даже эмулятор. Диаметрально противоположный вариант – Hi-Def NES, адаптер, устанавливаемый в оригинальные приставки и добавляющий HDMI-выход с опциональным сглаживанием HQx, все дополнительные звуковые чипы и многие другие возможности, обычно присущие эмуляторам. В некотором роде этот проект стирает грань между реальным железом и эмуляторами.