Обзор графической архитектуры Sega Mega Drive
Центральный процессор
ЦП у сеги был Motorola 68000 (m68k) первого поколения. Несмотря на то, что он называется нередко 16–битным, это не совсем соответствует действительности. В действительно архитектура m68k с самого начала описывает полноценный 32–битный процессор с полноценными 32–битными операциями и линейным доступом к 32–битному адресному пространству (в первых моделях выведены наружу были только 24 линии шины, но сами адреса всегда были 32–битные и в регистрах и в памяти). Помимо этого он имел 16 (шестнадцать) 32–битных регистров общего назначения! Честно говоря, как по мне, так даже архитектура i386 выглядит немного убого по сравнению с m68k, по крайней мере в те времена. Но почему же m68k назывался 16–битным, а маркетологи сеги нигде не протащили волшебное словосочетание «32 бита»? Потому что в самых первых линейках 16–битным было арифметико–логическое устройство этого процессора — из экономии или из каких–то других соображений, но складывал (и делал прочие операции в АЛУ) 32–битные данные этот процессор в два захода — по 16 бит за раз, что снижало производительность и не давало повода говорить об «32–битной мощи». Однако сложение адресов — например при индексации массивов — уже в первых моделях было 32–битным (АЛУ при этом не задействовалось — складывал адреса блок собственно адресации). Более того, когда более поздние модели m68k обзавелись 32–битными АЛУ — они выполняли уже существующий код с удвоенной скоростью без необходимости перекомпиляции — для программиста 16–битность АЛУ сразу была вещью «за ширмой», скомпилированный код сразу был готов к полноценной 32–битности.
Поэтому у Sega Mega Drive и карта памяти была абсолютно линейной — в самое начало (адреса от $000000 по $400000) отображалось ROM картриджа (максимально — 4Мб), в самом конце (адреса от $FF0000 по $FFFFFF) сидели 64Кб ОЗУ, а между ними болтались неиспользованные диапазоны и порты ввода–вывода.
У конкурента же (вышедшего позднее) — SNES, был настоящий 16–битный процессор Ricoh 5A22, который является немного переработанным клоном WDC 65816, который в свою очередь является 16–битным потомком 8–битного MOS 6502 — того самого, который был ЦП в Famicom/NES/Денди. 16–битность тут подразумевала, например, сегментацию доступа к памяти и прочие неудобности в связи с чем интересно было прочитать один параграф из интервью 1993–его года с новоиспеченной тогда компанией Treasure: http://megadrive.me/2011/11/03/an-interview-with-treasure/
Компания эта вышла из Konami чтобы сделать для Sega Mega Drive нетленку Gunstar Heroes и в этом интервью с их боссом Masato Maegawa, уже после выхода игры, проскакивает такой вот момент (далее мой перевод):
Вопрос: Konami — большой партнёр Nintendo, почему же вы теперь делаете игры для Sega?
Ответ: Меня всегда очаровывало железо. Люди постоянно сравнивают Mega Drive со SNES, говоря, что у SNES больше цветов и тому подобное…
Но Mega Drive имеет процессором 68000, с которым программисту очень легко работать. Я был программистом годы, делая игры для SNES, и поверьте мне — железо это боль в заднице. Если потребитель смотрит на неподвижные скриншоты, то он может подумать, что SNES лучше, но на самом деле если бы вы попытались впихать Gunstar Heroes на SNES, то нифига бы не вышло. Видите этих боссов? На SNES они бы начали тормозить, эти движения требуют слишком много вычислений. Это может быть сделано только на железе Sega.
(Gunstar Heroes на Sega Mega Drive)
Не исключено, конечно, что это был своего рода пиар для выходящей игры, тем не менее было такое мнение. Впрочем, своё «авторитетное мнение» по теме специальной олимпиады «что круче — Sega Mega Drive или Super NES» я уже давно сформировал и поделюсь им с вами попозже — после аналогичной статьи про собственно SNES, а пока перейдём к графическому чипу.
Video Display Processor (VDP)
Разрешение в пикселях 320x224 в NTSC версии или 320x240 в PAL версии.
VDP имеет 64 Кб выделенной памяти, ЦП к ней может обращаться только через порты ввода–вывода или посредством DMA.
DMA может передать из памяти ЦП (ROM картриджа или RAM ЦП) в память VDP (VRAM) или из VRAM в VRAM, включая режим заливки.
Вообще VDP является сильным апгрейдом видеочипа из 8–битной консоли Sega — Sega Master System, который в свою очередь является апгрейдом видеоконтроллера Texas Instruments TMS9918. В чипе поэтому присутствуют разные видеорежимы, но все они являются устаревшими и в играх на консоли не используются. Поэтому ниже будет описание единственного интересного «16–битного» видеорежима.
Палитра состоит из 64 слотов в отдельной памяти (Color RAM или CRAM), каждый слот в формате RGB содержит всего 3:3:3 бита, что ограничивает все возможные цвета всего 512 значениями и не более 64 цвета на экране за раз. Хотя есть еще спец–эффект затенения/просветления, что немного расширяет цветовые диапазоны, но в ограниченном числе мест применения. Так как все тайлы всегда 16–цветные, а палитра содержит 64 слота, то её можно воспринимать как массив из 4–х независимых 16–цветных палитр — в спрайтах и плитках фона (в каждой отдельно) содержатся 2ух–битные селекторы из этих 4–х палитр.
Тайлы 8x8 как фонов так и спрайтов располагаются с начала VRAM в 16–цветном режиме (4 бита на пиксель), что требует 32 байта на тайл 8x8 и таким образом всё VRAM может восприниматься как массив из 2048 тайлов. Поэтому индексы тайлов указываемые как в плитках фона так и в спрайтах 11–битные, что позволяет как раз указывать 2048 значений. Нулевой цвет в тайлах обозначает прозрачность, поэтому фактически тайлы хранят 15 цветов — и только если ни спрайты ни плитки фона не закрасили пиксель, то отображается цвет «заднего фона» — его индекс в палитре задаётся в специальном регистре.
Вся VRAM не может быть занята тайлами — нужно еще выделить место под собственно таблицы плиток фонов, таблицу спрайтов и разные вспомогательные вещи — как правило их местоположение внутри VRAM может в широких пределах меняться программированием портов VDP.
Спрайтов возможно 80 штук — их описания хранятся в таблице спрайтов в VRAM — это массив из 8–байтных элементов. Размерности спрайтов по каждой из осей (независимо) могут составлять от 1 до 4 плиток, таким образом от 8 до 32 пикселей. В каждом сканлайне может быть отрисовано не более 20 спрайтов. Забавно что спрайты образуют связный список — каждый хранит в себе индекс следующего спрайта к отрисовке, последний хранит 0.
Всего возможны 2 независимо скроллируемых задних фона/слоя (A и B), которые из себя представляют двумерные массивы плиток по 2 байта на плитку — 11 бит на индекс тайла, 2 бита на селектор палитры, 2 бита на вертикальное/горизонтальное зеркалирование и еще 1 бит на порядок приоритета. Слои плиток могут иметь незавимимые размерности по вертикали и горизонтали кратные 32, но общий объём каждого слоя не должен превышать 8Кб. При скроллинге на экране они проворачиваются через края, что позволяет реализовывать неограниченный скроллинг, как это на консолях принято.
Но сам скроллинг реализован довольно таки любопытно.
Горизонтальный скроллинг каждого из слоёв может производится одним из трёх вариантов на выбор:
1а) указывается смещение прокрутки для всего слоя как целого
1б) указывается смещение прокрутки для каждой линии тайлов (массивом)
1в) указывается смещение прокрутки для каждого сканлайна (тоже массивом)
Вертикальный скроллинг так же предлагает варианты:
2а) смещение всего слоя как целого
2б) массив из 20 смещений отдельно для каждых смежных двух колонок тайлов (то есть образуется 20 столбцов на экране с независимым скроллингом), причём так, как к этому массиву требуется обращаться каждые 16 пикселей, то он для скорости вынесен в отдельный блок памяти — так называемый VSRAM (Vertical Scroll RAM).
Комбинированием типов скроллинга можно добиваться визуально чего то весьма похожего на Mode 7 из SNES, хотя и в гораздо более скромных масштабах.
Вот, например, первый босс из игры Puggsy (перемотать на 0:15):
Кроме того присутствует еще одна любопытная деталь — псевдослой «WINDOW», который можно прилепить к любому краю слоя A и который будет перебивать его изображение и при этом никак не скроллится — что предполагалось использовать для всяких прилепленных к краю экрана статус–баров и прочих окошек с информацией и текстом.
Если сложить это с «посканлайновым скроллингом», то становится ясно, что разработчики видеочипа всячески пытались его программистов избавить от необходимости использовать технику HBlank–отсечения. Тем не менее, судя по интернету, псевдослой WINDOW мало кем использовался.
Таким образом, если сравнивать с Famicom/NES/Денди, то графика в 16–битной Sega Mega Drive это «x2» основных характеристик — два задних фона вместо одного, четыре бита на пиксель вместо двух и более тысячи тайлов в кадре вместо 512.
27 комментариев
У SMD же всегда всё 16-цветное и всегда тетрады бит плотно упакованы в один байт для пары соседних пикселей.
Проц на мой взгляд должен быть объективно быстрее и удобнее в программировании — семейство 6502 жутко неортогональное, 16-битный вариант использованный в SNES использует сегментацию памяти, 3 регистра общего назначения, постоянно надо лазить в память для получения второго аргумента арифметико-логической команды — из-за чего маппинг ПЗУ картриджа с ОЗУ представляет собой некоторую кашу для упрощения этих вещей…
В Sega 32-битная архитектура, плоская память, 16 высокоортогональных регистров общего назначения — я в принципе поверю, что даже если вычислительная мощь была сопоставимая, то всё равно программировать на этом WDC по сравнению с этим Motorola было «болью в заднице».
Опять таки, пару демок что видел на ютубе SMD vs SNES — у SMD динамический рендер обладал меньшей цветностью — мне показалось что как раз из-за размазки их по битплейнам. Хотя Mode 7 возможно можно раскрыть в более интересные эффекты, но там не применялось.
SMDдинамический рендер обладал меньшей цветностью..."имелся ввиду SNES
Лично мне гораздо больше нравится писать под 65816, чем под 68000, никакой боли. Для понимания надо просто попробовать, трудно объяснить на словах, почему 3 РОН (на самом деле не сильно-то они общего назначения, по сути РОН всего один) не являются проблемой. Так только кажется, если смотреть издали, с других колоколен (8080 и 68K). Если искать проблемы в архитектуре 6502, это прежде всего ограниченность косвенной адресации, т.к. если надо иметь полностью динамический указатель для двух вещей одновременно (типа источник и приёмник в LZ-распаковщике), начинаются пляски с регистром Y (косвенная адресация всегда идёт по (ZP),y).
Вычислительная мощь однозначно сопоставимая, никаких сомнений у меня нет. Несопоставимо — это когда разница в разы, такого тут и близко нет. Реальная боль в заднице на SNES — вовсе не процессор, а видеоконтроллер, в особенности система спрайтов. Ладно битпланы, но ограничение на 1024 тайла на слой фона, только два формата спрайтов одновременно, только 512 тайлов на спрайты (!!!!), расположение тайлов спрайтов, разбросанные по двум спискам биты OAM, и много чего ещё — вот это реально очень напрягает.
У Nintendo с опытом было похуже. Вообще инженерная работа была проделана очень хорошая, в том плане, что огромная куча наворотов очень аккуратно и красиво организована в наборе регистров, и предусмотрены некоторые очень полезные и крутые фичи (HDMA, window mask, color math). Но там видимо сильно давил прошлый ограниченный опыт и идея сделать обратную совместимость, которую на полпути отбросили, а наследие осталось и сильно повредило результату: такие же карты тайлов, как на NES (ещё одна местная боль), крайне ограниченные спрайты и прочие странности. В общем-то даже 65816 и SPC700 скорее всего были выбраны по причине начального курса на обратную совместимость и простоту перестройки программистов на новую платформу (SPC700 тоже очень похож на 6502, но дурные официальные мнемоники маскируют этот факт).
Сега же сделали один видеорежим с максимумом сбалансированных возможностей и успокоились на этом. Согласен, что слабая цветность — весьма неудачный и самый наверное неудачный шаг для своего времени. Полупрозрачность может быть только еще была бы интересной фичей.
Демка явно предшественница двух последующих, которые я не могу найти. В одной была графика прямо из GH, в другой очень стрёмная оригинальная графика с какой-то девочкой-роботом. В целом, можно видеть, что ещё 8 лет назад у него без проблем получалось сделать огромную кучу объектов и составные анимации без замедлений.
Имхо всё же Маегава если и преувеличивал, то не сильно. Плоский прямолинейный проц с 32-битными операциями (пусть даже под капотом выполняющимися как 16-битные, но в системе команд команда одна и как бы 32-битная) и простые раскладки видеопамяти, включая таблицы спрайтов, очевидно должны сделать программирование сложных сцен и трюков на SMD проще. И даже если на SNES можно добиться таких же показателей — то это уже демосцена с выдавливанием соков, а не SMD — нет, просто сел и запрограммировал. Другое дело, что в SNES было еще много аппаратных интересностей просто отсутствующих в SMD.
Самая главная проблема на SNES — откровенно неудачная система спрайтов. Если не сделать менеджер списка спрайтов грамотно, будут тормоза, наблюдаемые во многих играх. У большинства программистов тех лет просто не было опыта и времени разбираться, как решать эту проблему (но некоторые решали). Не выглядящие крутыми демки также показывают, что проблема решаема, а блог описывает, как именно. Я также пришёл к подобному решению, увидел изыскания этого товарища уже после этого.
Имхо, напротив, ожидаю увидеть на SNES всякие трюки с переходом к 8.8 как в статье и всякие таблицы с xor-ами для выдавливания соков из-за дурацких раскладок против абсолютно прямолинейного ремесленного кода на SMD в 16 РОН и адресацией любого куска памяти через base+size*offset. То что внешне можно добиться схожей картинки как раз не показатель. Но тут смысла нет препираться особо пока не будет реальных сравнительных опенсорцных тестов это всё «личные мнения».
Когда ты пишешь под SNES, ты не думаешь — а вот есть другой процессор, ах, сколько там РОН, какие там 32-битные операции и смещения. У тебя есть конкретный процессор, такой, какой он есть. Это данность. И ты пишешь для него код, как умеешь, настолько эффективно, как умеешь, чтобы решить конкретную задачу. Практика показывает: все задачи нормально решаются и там и там. Конечно, если у тебя опыт программирования на Z80 или M68K, ты будешь думать — как неэффективно писать на 6502/65816, там так мало регистров. Потому что у тебя нет понимания, что там принципиально другая парадигма, что там просто пишешь иначе, но в результате получаешь те же результаты. Когда ты уже знаешь и умеешь, ты не думаешь, как это сложно в сравнении, потому что тебе уже не сложно это делать.
Я много раз предлагал всем желающим поучаствовать в написании сравнительных тестов, Z80/6502, M68K/65816. Желающих за много лет почему-то не нашлось ни разу. И думаю, это не сработает, потому что знающий только одну архитектуру будет продолжать придерживаться своих взглядов на эффективность, т.к. по прежнему не понимает другую сторону ('ах, ты добился такого же результата, но мне не нравится твой непонятный код, я считаю его некрасивым'), а у знающего обе вопросов про сравнение уже не возникает.
Но не ради холивара, а ради прикосновения к красивому — в комментариях к этой же статье моей на другом ресурсе проскочила демка на SMD:
от прошлого года и в титрах авторы явно подзадоривают к «proper SNES competition» :)
там же привели и пару демок от SNES, но они страдали в сравнении как раз низкой цветностью слоёв с динамическим рендером — и я склонен считать что это из-за многократно уже оговоренных проблем с раскладками памяти и прочим.
Еще раз повторюсь — не холивара ради, но было бы интересно посмотреть бест оф зе бест.
Жмах
А вообще я тоже за подобные вводные уроки по «хитростям» 6502.
Это к тому, что мне просто по образу мышления не приходят в голову абстрактные типовые задачи. У всех платформ помимо процессора также и своя специфика во всём остальном, в частности в видеосистеме, где в основном критична скорость кода. Где не критична, там я особо не оптимизирую, пока не возникает реальная необходимость, потому что важнее закончить проект в целом и не перегореть (make it work, make it pretty), чем закопаться с головой в несущественных для общей картины деталях. Как правило в целой игре не так уж много узких мест, где нужно выжимать максимум.
Наверное, для тестов производительности нужно придумать виртуальную систему, где всё железо одинаковое, разный только процессор. Например, для удобства, Pentagon (потоиу что полностью софтовая графика, есть необходимость жёсткой оптимизации, и нет торможения по всей памяти), но с 6502 на 1.75 МГц. Для начала можно посмотреть предельную пропускную способность, развёрнутый цикл, задачи максимально быстрой очистки экрана и максимально быстрого заполнения экрана произвольной графикой (как мы делаем во фреймовых листалках):
Очистка (lps — сколько раз выполнится такой за секунду на соответствующей частоте процессора):
Разница в скорости на треть. В размере очень большая, что ожидаемо — код на 6502 всегда заметно многословнее.
Зарисовка:
А вот в копировании разница уже не такая большая, всего 6 условных единиц.
Эти тесты не показывают ничего конкретного, кроме одного: не всё так однозначно, как кажется. Где-то проигрыш 6502 очень заметен, где-то результат очень близкий. По практическим задачам (эффекты на ZX и C64, порты биперных движков с ZX на Atari) мы давно уже знаем, что разница по мере роста сложности алгоритмов сводится к минимуму, всюду возможно примерно одно и то же.
Ну не снимает, а скорее как то меняет — и еще надо смотреть как. Технически всё-равно надо успеть сформировать строку.
У спец-регистров в принципе bandwidth может как бы и не быть, то есть загрузив их в начале строки можно как бы «бесплатно» не пересекаясь в них лазить хоть каждый пиксель — на то они и регистры.
Но в любом случае победил в высокопроизводительных игровых системах окончательно блиттинг, причём в весь сюрфейс сразу и во много потоков, а роль видеоадаптера свелась к неспешному скармливанию в видеопорт уже готовых полностью пикселей.
Пиксельный буфер в импортных приставках мне не припоминается нигде. Впервые я увидел эту идею в V2Z80 (который задолго до V6Z80P), который из ранних 2000-х, и она показалась мне очевидной и логичной. Чуть позже узнал, что и в советском игровом автомате ТИА-МЦ-1 ~1988 года спрайты тоже сделаны с буфером строки, там даже три буфера 256x4 (16-цветные спрайты). А потом уже стал вникать в эти детали по популярным приставкам, и удивился, что там сделано совершенно иначе.
Реги спрайтов: 4*32*20=2560 бит
Пикс. буфер: 9*320*2=5760 бит
Sergey, [26.09.18 10:40] А буфер строки можно на драме сделать
Помню, я смотрел видеощупом сигналы на видеопамяти (на обоих портах) и были они очень странные, логику я понять не смог.