1. Штатные возможности CGA
У обывателя CGA обычно ассоциируется с графикой 320x200 и четырёхцветной палитрой с кислотно-жуткими цветами, как например в играх 1983 г. Alley Cat, J-bird, Lode Runner и Tapper:
Но ограничение 320x200x2bpp накладывается лишь объёмом имеющейся на адаптере видеопамяти (16 Кб), фактически же он способен производить изображение 640x200x4bpp — с пикселями, сильно вытянутыми вертикально. Это используется, например, в текстовом режиме 80x25 с 16-цветными символами и фоном, и символьной матрицей 8x8 пикселей. (Можно посчитать, что буфер экрана при этом занимает всего 80x25x2 = 4000 байт.) Но если в этом «высоком разрешении» 640x200 выводить графику, то 16 Кб видеопамяти хватит лишь на чёрно-белое изображение. Такой графический режим поддерживала, например, Windows до версии 3.0 включительно:
На выходе CGA производил цифровой сигнал, для которого специально был придуман интерфейс RGBI: две линии синхронизации сигнализируют о начале кадра и о начале строки развёртки, а четыре линии видеосигнала передают четырёхбитный цвет текущего пикселя. Но мониторы, способные принимать на входе цифровой сигнал RGBI, в 1981 г. были редкостью; сама IBM начала производить такие цифровые мониторы только в 1983 г. Для домашних пользователей CGA имел второй вывод — тот самый композитный видеосигнал, с которым колдовали мастера демо-сцены. Его можно было подключить через RF-модулятор к антенному входу обычного американского телевизора, плюс некоторые телевизоры имели специальный композитный вход (для подключения, например, видеомагнитофона), который позволял вводить видеосигнал в обход тюнера и избежать ненужной модуляции/демодуляции.
Часть обвязки видеоадаптера, формирующая из цифрового вывода композитный сигнал NTSC (это американский стандарт цветного телевидения), была предназначена лишь для переходного этапа, пока все пользователи ПК не обзаведутся цифровыми мониторами. Следующий видеоадаптер от IBM — EGA (1984) — поддерживал только цифровой (уже шестибитный) вывод, задействуя в интерфейсе RGBI пару до тех пор неиспользуемых контактов. Кроме того, режимы «высокого разрешения» не рекомендовались для использования с композитным видеовыводом, потому что 640 пикселей в строке развёртки превышали возможности NTSC. Следующий раздел объясняет, почему; а Википедия иллюстрирует, как размыливался текст в 80-колоночном режиме CGA на мониторе с композитным вводом:
2. Возможности телевизора
Чёрно-белое телевидение было устроено весьма прямолинейно: электронный луч обходит экран строка за строкой, а уровень видеосигнала напрямую управляет мощностью луча. На время обратного хода между строками и между кадрами луч выключается, а видеосигнал передаёт импульсы синхронизации. Поскольку сигнал полностью аналоговый, его горизонтальное разрешение не определено: яркость может плавно меняться на всём протяжении строки. С другой стороны, вертикальное разрешение жёстко задано частотой строчной развёртки (243 строки для NTSC).
Иллюстрация, заботливо нарисованная в MS Paint, показывает, как выглядит видеосигнал для кадра, состоящего из двух вертикальных полос: широкой тёмной и узкой яркой. В верхней части схематично показан сигнал для одной строки, в нижней — сигнал для последовательности строк. Перед каждой строкой передаётся синхроимпульс, который «чернее чёрного».
Когда создавалось цветное телевидение, то было важно, чтобы уже существующие чёрно-белые телевизоры приемлемо отображали цветной видеосигнал. Американцы придумали следующий трюк: информация о цвете передаётся поднесущим гармоническим сигналом, прибавляемым к сигналу яркости. Тогда чёрно-белый телевизор, не умеющий выделять и анализировать поднесущую гармонику, будет подсвечивать каждый пиксель с яркостью, соответствующей среднему значению сигнала за время передачи пикселя. Если поднесущая частота достаточно большая, то добавочный сигнал не будет влиять на среднее значение, т.е. на отображаемую яркость. Теперь договоримся, что фазовый сдвиг поднесущей соответствует оттенку цвета, а её амплитуда — насыщенности; разделение цвета на яркость, оттенок и насыщенность соответствует цветовой модели YIQ.
На второй иллюстрации схематично изображён видеосигнал для одной строки кадра, состоящего из цветных вертикальных полос. Между синхроимпульсом и началом строки дополнительно передаётся color burst, который настраивает телеприёмник на фазу, от которой будут отмеряться сдвиги поднесущей. Теперь плоские участки сигнала (т.е. с нулевой амплитудой поднесущей) будут соответствовать серым полосам (нулевой насыщенности); участки, где поднесущая соответствует по фазе color burst — жёлтым полосам с двумя разными значениями насыщенности в зависимости от амплитуды поднесущей; участок, где поднесущая в противофазе относительно color burst — синей полосе. Поскольку средний уровень сигнала на всех участках одинаковый, то чёрно-белый телеприёмник отобразит все полосы одним и тем же оттенком серого.
NTSC предписывает, чтобы добавочный сигнал был синусоидальным, но это не так важно: показанная на иллюстрации прямоугольная волна (меандр) состоит из гармоники с нужной частотой и фазой, а также более высокочастотных добавок, игнорируемых телеприёмником. Амплитуда этой гармоники больше амплитуды меандра в раз, но поскольку телевизор использует амплитуду поднесущей не по абсолютному значению, а относительно амплитуды, выделенной из color burst — то на отображение цвета не влияет замена синусоидальной добавки на прямоугольную. CGA и прочие примитивные цифровые устройства генерируют именно прямоугольные волны.
Для того, чтобы упростить разделение основного сигнала (яркость) и добавочного (цвет), NTSC стандартизовал поднесущую частоту, в 227.5 раз большую, чем частота строчной развёртки — т.е. на каждую строку вместе с промежутком между строками приходится 227.5 периодов поднесущей. Таким образом, у американского цветного телевизора есть определённое горизонтальное разрешение: яркость по-прежнему может меняться плавно на всём протяжении строки, но частота изменения оттенка цвета ограничена поднесущей частотой. Если телевизор получает видеосигнал с горизонтальным разрешением в 640 пикселей, то каждому пикселю соответствует примерно треть периода поднесущей, и примитивный аппаратный фильтр не успеет для каждого пикселя корректно выделить из сигнала фазовый сдвиг и амплитуду поднесущей. Это приводит к неожиданным результатам — от размыливания, как показано в примере выше, до создания «артефактных» цветов, недостижимых штатными средствами видеоадаптера. (Мне крайне не нравится название «паразитные цвета», использованное SLY_G в переводе статьи про 1024 цвета; основное значение английского слова artifact — «рукотворный, созданный искусственно» — противопоставляет эти цвета «естественным» для CGA, и не имеет никакого отношения к паразитизму.)
Именно с превышением горизонтального разрешения был связан аппаратный баг в CGA: в режимах 640x200 (в т.ч. в 80-колоночном текстовом) изображение на композитном мониторе оказывалось чёрно-белым. Обход этого бага был хорошо известен: надо задать вокруг изображения коричневую рамку. Дело в том, что обвязка, формирующая синхросигналы, задаёт их длительность в тактах видеоадаптера; в режимах 640x200 эти такты вдвое короче, чем в режимах 320x200, так что и синхросигнал между строками кадра получается вдвое короче, чем того требует NTSC. В результате color burst просто не успевает передаться. Но если вокруг изображения есть коричневая рамка (у коричневого цвета почти нулевой фазовый сдвиг, маленькая яркость, и насыщенность соответствует примерно подходящей для color burst амплитуде), то монитор принимает левую сторону рамки за color burst, не отрисовывает её на экране, и корректно настраивается на фазу цветовой поднесущей.
3. Устройство композитного сигнала CGA
Хотя цифровой видеосигнал, производимый CGA, может иметь горизонтальное разрешение до 640 пикселей (т.е. 640 изменений уровней цифровых сигналов в одной строке) — композитный видеосигнал можно представлять себе как имеющий разрешение 1280 «субпикселей» независимо от используемого видеорежима, т.е. на каждый пиксель в режимах 640x200 приходится по два субпикселя, в режимах 320x200 — по четыре. Уровень сигнала (в вольтах), выводимый видеоадаптером для текущего субпикселя, вычисляется по формуле , где бит I (intensity) берётся непосредственно из цвета текущего пикселя, а сигнал C (chroma) выбирается мультиплексором в зависимости от трёх остальных битов цвета (R, G, B):
R | G | B | C | цвет | |
---|---|---|---|---|---|
0 | 0 | 0 | 00000000… |
чёрный | |
0 | 0 | 1 | 00001111… |
синий (~180°) | |
0 | 1 | 0 | 11100001… |
зелёный (~45°) | |
0 | 1 | 1 | 11000011… |
бирюзовый (~90°) | |
1 | 0 | 0 | 00111100… |
красный (~270°) | |
1 | 0 | 1 | 00011110… |
пурпурный (~225°) | |
1 | 1 | 0 | 11110000… |
жёлтый (~0°) | |
1 | 1 | 1 | 11111111… |
белый |
Таким образом, из 227.5 периодов поднесущей в строке — 160 приходятся на выводимое изображение, а остальные — на промежуток между строками (синхроимпульс, color burst, и рамка вокруг изображения). Важно, что от color burst до первого пикселя строки проходит нецелое число периодов поднесущей: первый пиксель начинает передаваться на фазе 45°, так что если строка начинается с четырёх жёлтых пикселей, то сигнал chroma во время передачи соответствующих восьми субпикселей будет принимать значения
11100001
.Яркость пикселя (luma), т.е. среднее значение уровня сигнала за период поднесущей, получится , где : 0.5 для хроматических цветов, 0 для чёрного и 1 для белого. Это означает, что на чёрно-белом композитном мониторе первая модель CGA (1981) могла отображать только шесть оттенков серого. В более поздних моделях CGA (с 1983) непериодическая добавка к поднесущей стала зависеть от всех четырёх битов цвета, так что яркость соответствовала более сложной формуле , что позволило на чёрно-белом мониторе для каждого из 16 значений цвета отображать свой собственный оттенок серого. На цветном композитном мониторе в результате этой доработки тоже стали отображаться цвета, различающиеся по яркости и более близкие к отображаемым на цифровом:
Аппаратные баги и недоработки в обвязке, формирующей из цифрового вывода композитный сигнал, свидетельствуют о том, что разработчиков CGA не слишком волновало его использование с композитным монитором, и ещё меньше волновало, как на композитном мониторе будут отображаться режимы «высокого разрешения». Тем не менее, именно это неподдерживаемое сочетание позволило мастерам демосцены выжать из CGA невообразимое!
4. Неожиданные возможности CGA...
4а) …в графических режимах
Как объяснено выше, часть строки, закрашенная одним цветом, в композитном видеосигнале превращается в меандр с периодом в восемь субпикселей. Верно и обратное: любой композитный сигнал, повторяющийся с периодом в восемь субпикселей, будет отображаться как одноцветная часть строки, потому что выделенная из него поднесущая гармоника будет иметь постоянный фазовый сдвиг и постоянную амплитуду. Каким именно будет используемый фазовый сдвиг, предсказать сложнее; статья про 1024 цвета показывает результат безо всякого объяснения:
В столбце Nybble показана повторяющаяся последовательность из четырёх пикселей в режиме 640x200x1bpp, т.е. из восьми субпикселей. Две из этих последовательностей (
0101
и 1010
) соответствуют меандрам с периодом в четыре субпикселя: поднесущей гармоники в такой волне нет вообще, так что цвет в результате получается серым. Ещё четыре последовательности (0011, 0110, 1001, 1100
) соответствуют чистым волнам поднесущей частоты — со сдвигом в 135°, 225°, 45° и 315° соответственно. Две из этих волн задают стандартные пурпурный и зелёный цвета, две оставшиеся — голубой и оранжевый, недостижимые штатными средствами CGA. Заполнение сигнала в 75% (последовательности 0111, 1011, 1101, 1110
) соответствует сдвигу поднесущей гармоники по фазе на полпикселя, т.е. на один субпиксель; но результат не совпадает точно с фазой стандартных цветов — из-за тех самых схем задержки, которые «подтягивают» стандартные цвета CGA к стандартным цветам NTSC. Таким образом получаются оттенки с небольшим сдвигом: бирюзоватый синий, зелёноватый бирюзовый, красноватый коричневый, пурпурноватый красный. Заполнение в 75% увеличивает средний уровень сигнала, т.е. яркость цвета; но не влияет на насыщенность, потому что более широкие «края» меандра создаются высокочастотными гармониками, игнорируемыми телеприёмником. Наконец, сигнал с заполнением в 25% соответствует тем же самым оттенкам, что и для 75%, но меньшей яркости.Ещё сложнее предсказать, какой цвет получится, когда повторяется последовательность не из чёрных и белых пикселей, а из цветных. В режиме 640x200x1bpp цвет фона всегда чёрный, но второй цвет можно выбрать любой из 16 стандартных. В режиме 320x200x2bpp, наоборот, цвет фона можно выбрать любой из 16 стандартных, а три других цвета определяются палитрой, выбираемой из четырёх вариантов. В статье про 1024 цвета показаны две такие комбинации, опять же безо всякого объяснения. Я обратился за комментариями непосредственно к reenigne, ответственному за техническую часть видеовывода в 8088 MPH, а также реализовавшему эмуляцию CGA в DOSBox; и он посоветовал мне cgaart — написанный им автономный эмулятор CGA, способный работать в пакетном режиме. Добавив к cgaart несложную обвязку на Python, я сгенерировал таблицу из всех возможных 16-цветных наборов «артефактных цветов»:
Код каждого набора состоит из номера графического режима (
1A
для 640x200x1bpp, 0A
для 320x200x2bpp), номера палитры (0–3
), и номера свободно выбираемого цвета (0–F
). Каждая строка таблицы состоит из 16 квадратиков, соответствующих повторяющейся последовательности пикселей; каждый квадратик состоит из пяти полос — цифровой вывод, уровень композитного сигнала и вывод на композитном мониторе с первой моделью CGA, вывод на композитном мониторе и уровень композитного сигнала с исправленной моделью CGA. Уровень композитного сигнала показан средний за два субпикселя — cgaart рассчитывает его лишь с такой точностью. Рассмотрим подробнее строки, соответствующие двум наборам, показанным в статье про 1024 цвета — это наборы 1A0C
и 0A01
; но перед этим рассмотрим более простой набор 1A04
:Набор
1A04
— красные пиксели на чёрном фоне — соответствует сигналу с заполнением 50% и сдвигом 270°, модулируемому периодической последовательностью из четырёх пикселей. Высокий уровень сигнала приходится на первые три пикселя последовательности, поэтому её четвёртый бит вообще не влияет на получающийся цвет: так, 000x
и 001x
превращаются в чистый чёрный, а 111x
— в чистый красный. Последовательности, обнуляющие первый пиксель (010x
и 011x
) соответствуют сдвигу фазы на один субпиксель, т.е. на -45°, к пурпурному; обнуляющие второй пиксель (100x
и 101x
) — сдвигу на 45° к оранжевому.Набор
1A0C
— светло-красные пиксели на чёрном фоне — соответствует тому же самому сигналу, но с непериодической добавкой, так что его низкий уровень теперь выше чёрного. В результате этого последовательности 0001
, 0010
, 0011
превращаются в те же оттенки (зеленовато-бирюзовый, бирюзовато-синий и голубой), как и при белых пикселях, но теперь с намного меньшей яркостью, потому что низкий уровень исходного сигнала играет роль высокого уровня для получающегося. Остальные последовательности сохраняют высокий уровень исходного сигнала, и соответствующие им оттенки цвета получаются близкие к набору 1A04
, хотя яркость теперь выше за счёт более высокого среднего уровня.Наконец, набор
0A01
— синий фон, зелёно-красно-коричневая палитра, четыре субпикселя в каждом пикселе, два пикселя в повторяющейся последовательности — содержит четыре стандартных цвета (последовательности 00, 11, 22, 33
не вызывают артефактов) и их попарные смеси: если сложить два меандра, то сдвиг поднесущей гармоники в получившемся сигнале будет посередине между сдвигами слагаемых. От порядка слагаемых, тем не менее, зависит яркость и насыщенность результата: например, у красного цвета высокий уровень приходится на субпиксели 1–4, у зелёного — на 6–1, так что последовательность 12
полностью отсекает высокий уровень красного слагаемого, и наполовину — зелёного; тогда как последовательность 21
полностью сохраняет высокий уровень красного слагаемого, и наполовину — зелёного. Оттенок в обоих случаях получается жёлтым, но для последовательности 12
— насыщенным и очень тёмным, тогда как для 21
— ненасыщенным и очень светлым.Итак, есть 80 наборов по 16 одновременно доступных цветов. Какое при этом фактическое разрешение? Цвет возможно менять только между блоками по 8 субпикселей, т.е. 160 раз за строку; но яркость задаётся для каждого пикселя независимо. Статья про 1024 цвета предостерегает, что эти режимы нельзя называть «16-цветными 160x200», потому что «горизонтальное разрешение – вопрос открытый, он зависит от семплинга и фильтрации сигнала, и меняется в зависимости от того, какие формы цветового сигнала вы используете» (перевод SLY_G). Тем не менее, легко убедиться, что даже при постоянном оттенке цвета композитным монитором не отображаются колебания яркости в пределах 8-субпиксельных блоков: обратите внимание, например, на набор
0A0A
в полной таблице, а в нём — на последовательности 01
и 10
, соответствующие зелёным пикселям на светло-зелёном фоне. И яркость, и оттенок цвета анализируются композитным монитором поблочно, так что вместо чередующихся зелёных пикселей двух уровней яркости получаются однотонные пиксели со средним уровнем яркости. Для последовательности 10
результат получается менее насыщенный, чем слагаемые, потому что эта последовательность отсекает низкий уровень тёмно-зелёного слагаемого. Поэтому, на мой взгляд, при использовании артефактных цветов есть все основания считать 160x200 фактическим разрешением.4а) …в текстовых режимах
Как упоминалось в самом начале, CGA поддерживает 16-цветный текстовый режим 80x25 с символьной матрицей 8x8 пикселей, и в этом режиме задействуется лишь четверть видеопамяти. Умельцы нашли недокументированный способ переключать вывод на новую строку текста через каждые две строки пикселей, т.е. выводить от каждого символа только две верхних строки пикселей. В получающемся 100-строчном «полутекстовом» режиме задействуются все 16 Кб видеопамяти, и изображение получается 16-цветным с разрешением 640x200 пикселей. Но по сравнению с графическими режимами, в полутекстовом действуют достаточно жёсткие ограничения: во-первых, в каждом знакоместе 8х2 пикселей могут использоваться только два разных цвета; во-вторых, последовательность «основных» и «фоновых» пикселей в каждом знакоместе должна соответствовать двум верхним строкам одного из 256 символов, прошитым в CGA ROM. Даже и с этими ограничениями, сочетающими неудобства пиксель-арта и текстовой псевдографики, мастерам удаётся создавать невероятные 16-цветные рисунки:
(В статье про 1024 цвета показана только четверть этого рисунка, но отдельно была опубликована и его полная версия — прямо в виде дампа видеопамяти.)
Без 100-строчного хака — т.е. если бы матрица каждого символа отрисовывалась полностью — средняя часть рисунка выглядела бы так:
Справа показана прошитая в ROM таблица символов, где верхние две строки каждого символа, доступные для использования в полутекстовом режиме, подсвечены жёлтым.
Впервые догадалась совместить 100-строчный хак и текстовую псевдографику фирма Macrocom, выпустившая в 1984 г. игру «ICON: The Quest for the Ring» с казавшимся невероятным для CGA разрешением 320x200x4bpp. Впрочем, даже статичные заставки у Macrocom были прорисованы гораздо хуже, чем у VileR; а во время игры графика использовалась ещё более примитивная.
(Парадокс в том, что хотя это текстовый режим, выводить в нём текст весьма нетривиально: приходится собирать каждый символ из «обрезков» прошитых в ROM.)
Ещё одно существеннное ограничение полутекстового режима — то, что на композитном мониторе изображение размыливается до довольно неприглядного вида. Например, рисунок VileR выглядел бы так:
Но из этого размыливания можно извлечь пользу — для генерации огромного числа артефактных цветов в полутекстовом режиме! Как объясняется в предыдущем разделе, в сплошной артефактный цвет превращается любая повторяющаяся последовательность пикселей с периодом в 8 субпикселей, т.е. в половину ширины символа в 80-колоночном режиме. Это значит, что в полутекстовом режиме — когда и основной, и фоновый цвет для каждого символа может выбираться произвольно из 16 стандартных цветов — для создания артефактных цветов понадобятся символы, у которых
- две верхние строки пикселей совпадают;
- левая и правая половины этих двух строк совпадают.
В CGA ROM нашлись символы, подходящие для двух периодических последовательностей —
1100
и 0110
. Первая последовательность позволяет получить артефактные цвета, соответствующие любым двум повторяющимся пикселям в режиме 320х200, т.е. любые цвета из строк 0Axx
в полной таблице; вторая — цвета из квадратиков №6 и №9 в строках 1A0x
, когда последовательность 0110
выводится цветными символами по чёрному фону либо чёрными символами по цветному фону, соответственно. Фактическое разрешение при этом получается 80x100: минимальная область, для которой можно задать цвет, соответствует «урезанному» знакоместу.Итак, сколько артефактных цветов можно одновременно отображать в полутекстовом режиме? Доступны все комбинации из 16 основных цветов, 16 фоновых, и двух подходящих символов, так что автор этого трюка восклицает: «512 цветов! Конечно, реально их меньше, встречаются дубли и очень мало отличающиеся оттенки.» Когда основной и фоновый цвета совпадают, то очевидно, что с любым символом результат получится одним и тем же, поэтому на цифровом выходе имеем 496 разных периодических последовательностей из восьми пикселей. Преобразование этого вывода в композитный приводит к тому, что некоторые последовательности превращаются в один и тот же видеосигнал; например,
0110
серым по чёрному, серым по пурпурному, и светло-зелёным по пурпурному — превращаются в один и тот же тёмно-сиреневый оттенок:По сравнению с полной таблицей, здесь квадратики для наглядности увеличены вдвое, что позволило подписать прямо на диаграмме значение каждой полосы внутри квадратиков. Видно, что последовательность
0110
полностью срезает высокий уровень пурпурного фона, так что фон становится неотличим от чёрного. Зелёный цвет отличается фазой от пурпурного на 180°, так что эта последовательность точно так же срезает высокий уровень зелёных символов, и светло-зелёные символы становятся неотличимы от серых. На таблице цветов, приводимой в статье про 1024 цвета, можно увидеть, что строки №0 и №5, соответствующие чёрному и пурпурному фону, почти неотличимы; так же почти неотличимы и столбцы №8 и №10, соответствующие серым и светло-зелёным символам. По расчётам cgaart, тёмно-сиреневые оттенки, соответствующие трём названным комбинациям параметров, совпадают полностью; с исправленной же моделью CGA, как видно в нижней части квадратиков на диаграмме, эти три оттенка получаются различными.Кроме этих трёх комбинаций, первая модель CGA одинаково отображает последовательность
0110
чёрным по светло-пурпурному и по серому; серый по светло-пурпурному даёт стандартный серый цвет, а чёрный по пурпурному — стандартный чёрный. Наконец, как показано в правой части диаграммы, последовательность 1100
серым по светло-красному даёт тот же — почти стандартный — оттенок серого, что и последовательность 0110
светло-зелёным по светло-пурпурному. Итого, с первой моделью CGA доступны 490 различных цветов, зато с исправленной — все 496.Кульминация статьи про 1024 цвета — придуманный reenigne способ отображать в полутекстовом режиме только одну верхнюю строку пикселей для каждого символа, зато повторять эту строку дважды. Это сложно назвать «новым графическим режимом», потому что требуется постоянное — в начале каждой строки развёртки — изменение настроек CGA, что занимает 8088 процессор полностью, и не оставляет возможности делать параллельно с отображением 1024-цветного изображения ещё что-либо — например, готовить анимацию. Но благодаря этому трюку, становится возможным использовать последовательности
0010
и 0101
впридачу к двум показанным выше. Пользуясь четырьмя разными символами, на цифровом выходе возможно получить 976 разных периодических последовательностей из восьми пикселей. А сколько получится различных цветов после преобразования в композитный видеосигнал? Я насчитал 950 с первой моделью CGA, и 973 — с исправленной.В завершение я привожу таблицу всех цветов, которые возможно получить в полутекстовом режиме. У VileR есть аналогичная таблица, но сгруппированная по типу монитора, а не по выводимой последовательности пикселей, как у меня. Его таблица удобна для художника, работающего с конкретным устройством; моя — для теоретика, желающего понять, каким образом на мониторе появляется тот или иной цвет.
Sabin
Хабр торт. Большое спасибо за статью и подробный разбор, как же это работало