Зачем?
Хочу в одной статье популярно рассказать про то, что за зверь такой - "компьютер ATARI". А конкретно речь пойдет о семействе ATARI XL/XE, включающее модели от 800XL до последнего XEGS (речь про пэкашки, а не приставки).
Хочу как на играх, так и на примерах на языке BASIC и ASM показать особенности графики ATARI. Немного упомяну о разных компьютерах и консолях на MOS 6502 и его аналогах. Совсем чуть-чуть субъективного мнения о том, каким бы я хотел видеть ATARI. При этом я понимаю, что создание идеального универсального устройства всегда возможно только постфактум, когда уже понятно что прижилось, что было востребовано, а что пылилось и не использовалось. Так как у современников поиски компромиссов основаны на технологиях и финансах того времени, но всё равно жаль что на свежих XE или XEGS при сохранении обратной совместимости не стали прокачивать графику.
В статье, говоря о 6502, я буду иметь в виду любое семейство этих процессоров, включая разные гибридные варианты - как HuC6280, который достаточно отличается от 6502, но сохраняет совместимость.
Для кого?
Любителям ретро-тематики, просто интересующимся аппаратной частью любых гаджетов, тем, кто хочет узнать именно про ATARI или тот кто играл в эти игры на эмуляторе (не обязательно на ПК, но и на множестве китайских "приставок"), но хочет узнать что-то о платформе и её возможностях или недостатках.
6502 в культуре
Who Am I?
Я не программист, не специалист по аппаратной части ПК как 80-х так и современных, и еще много других "не". Я просто увлечённый этой тематикой человек, это моё хобби, поэтому за возможные технические неточности, не влияющие на судьбу человечества, прошу прощения. Все замечания учту, статью подправлю, если в этом будет необходимость.
Знакомство в вычислительной техникой началось у меня в 1987 году с ATARI 130XE, параллельно с 1992 по 1995 общался с платформами на Z80 - самосборные клоны Spectrum. Вернулся к тематике ретро-ПК в 2015 году по сути разочаровавшись в эволюции IBM PC совместимого. Давно не могу найти отдушину в работе с современным железом, которая, по моему мнению, свелась к тому - насколько ярко у тебя светится RGB подсветка, поэтому развернулся на 180 градусов и "переехал" опять в 80-е. Так же интересна тематика Arduino, но именно здесь мы её не будем рассматривать.
Ну а современный ПК, как и положено, выполняет функцию рабочей машины - быстрый и функциональный. А эмоции дарит ретро железка. Ну и для программиста-любителя поиск решения на таком старом и медленном железе представляется более захватывающим.
Эмулятор Altirra
В статье есть материал который можно воспроизвести на реальном железе или в эмуляторе. Я пользуюсь эмулятором Altirra, там работает copy-paste, поэтому вы можете код из статьи вставлять в эмулятор и просто запускать, без необходимости набирать текст руками.
У меня есть реальное железо, но в большинстве случаев разработка чего-либо на нём не имеет смысла, например результат выполнения не получить в виде скриншота, потребуется плата захвата, композитный сигнал не самый красивый для мелких скриншотов. А делать пересъёмку с экрана я не хочу. Так же перенос кода происходит неудобно, да и файлы BAS не являются текстовыми. Плюс еще в скорости, эмулятор позволяет исполнять код быстрее, если это требуется.
Инструкция по настройке Altirra
качаем Altirra, запускаем, заходим в настройки System->Configure System. Слева выбираем Firmware и справа делаем примерно как на картинке
Нажимаем ОК и потом F5 - у вас должен загрузиться ПК в режиме Бейсика.
Для запуска примеров этого достаточно.
Если же захотите скачать игры и позапускать, то в меню File выбираете Boot Image, выбираете нужный файл с образом картриджа, дискеты или просто исполнимый файл XEX и если вам повезло, то через несколько секунд вы увидите игру. Рекомендую иметь джойстик или геймпад, хотя можно и маппить клавиатуру (Меню Input->Input Mappings, ставим галочку на первой строке Arrow Keys -> Joystick (port 1)).
Имейте в виду что при активном маппинге на кнопки у вас не будет работать управление курсором в Бейсике.
Так же рекомендую выписать/запомнить основные настройки на клавиши Start (F2), Select (F3), Options (F4), Reset (F5). С Reset мы познакомились выше. С помощью них производится настройка внутри игры, ну и запуск через Start, хотя в подавляющем большинстве игр Start и Trigger Button (мапа на Left Ctrl) работают одинаково. Если листинг после вставки слишком долго вставляется из буфера и вам стало скучно, то можно нажимая на F1 ускорять работу эмуляции, это позволит быстро вставить код.
Настроек очень много, Altirra не только позволяет играть, но и хороший помощник для изучения внутренностей игр.
Немного о BASIC - если вам не нужна какая-то строка, то чтобы её удалить нужно ввести её номер и нажать ENTER, чтобы вывести программу на экран можно набрать
LIST
или
L.
(точка работает для многих команд и используется для сокращения набора). Если нужно посмотреть конкретную строку или диапазон строк, то пишем
L.10,20
(точка для сокращения команды LIST
, а запятая указывает разделитель диапазона), то есть вывести все строки от 10 до 20 включительно, для отдельной строки пишем
L.13
(тут указываем номер интересуемой строки). Если хотите удалить строку временно, то можно использовать оператор REM или дать номер строке за пределами программы. Например у вас есть строка
10 GRAPHICS 9
И вы временно хотите её убрать из кода, можно написать поверх REM
10 REMPHISC 9
и получить странную строку, но интерпретатор ккогда увидит первые три буквы REM всё остальное уже не будет транслировать в код, В конце ввода обязательно нужно нажать ENTER
, пока не нажали ENTER
изменения не применяются. После команды L.10
вы увидите, что строка будет оформлена так
10 REM PHICS 9
то есть при выводе на экран текст будет отформатирован, чтобы вернуть строку в работу нужно будет REM
удалить сначала поставив пробел, а потом набрав начала слова GRAPHICS
, ниже на анимации я показал как это происходит. У ATARI есть механизмы для управления строками, такие как сдвиг вперед всей строки или подтягивание всей строки, но может быть это слишком лишняя информация для работы с кодом в рамках этой статьи.
Чтобы "удалить" программу из памяти нужно ввести команду NEW
, кнопка RESET не приводит к стиранию ОЗУ, поэтому с помощью RESET удобно сбрасывать (останавливать) замкнутые циклы в программах, особенно если они выполняются в графическом режиме, иначе BASIC после завершения программы вернёт управления в стандартный текстовый режим и вы не увидите результат выполнения.
Небольшой экскурс в историю продуктов на базе MOS 6502 и его производных
Собственно - почему в названии XL/XE пишутся чаще всего в таком виде. Дело в том что аппаратно все компьютеры этих серий имеют схожие характеристики и различия касаются только небольших возможностей видеочипа и объема ОЗУ. В качестве базовой (классической) машины принято считать модель 800XL с 64Кб ОЗУ на борту. Сегодня есть моды для увеличения памяти до 4 мегабайт, но стандартом тех лет были именно 64 Кб. При этом в линейке XE была модель 130XE которая имела на борту 128 Кб ОЗУ и было некоторое количество игр с переключением банков памяти под такой объем, но никакого серьезного влияния это не оказало, поэтому весь софт опирается на 64 Кб как на стандарт (даже в самой последней XEGS так же стоят 64 Кб).
В именованиях линейки XE используются такие числа как 65, 130 и 800, при этом 800 не является верхней моделью, по сути 800XE и 65XE это одна и та же модель только для разных рынков (интересно что в некоторых моделях 65XE используется плата от 130XE с нераспаянной памятью и прошитым контроллером на 64Кб ОЗУ, что многие используют для весьма несложного апгрейда, у меня как раз такая модель, апгрейдом я пока не занимался). Соответственно 800XE подразумевается как наследник 800XL уже в новом корпусе, 65XE показывает что оперативной памяти 64 Кб (65536 байт, убираем три цифры справа и получаем 65). Модель 130XE содержит 128 Кб ОЗУ и наверное было бы логично назвать модель или 128XE или 131XE, так как оперативной памяти 131072 байт), но выбрали число 130, как удвоенное 65. Была ли цель выделиться на фоне ZX 128 или Commodore 64 - сказать сложно.
На хабре есть статья как раз с уклоном на историю, ознакомление с ней будет более уместным, так как настолько подробно описывать историю 6502 в этой статье я не вижу смысла. Так же не стану дотошно перечислять всё-всё-всё, что было на этих процессорах, скорее затрону общим списком, возможно кто-то даже не знал об этом.
Для меня в своё время стало открытием, что компания ATARI - не японская. Во-первых я рос в годы, когда японская электроника была на пике, во-вторых - само слово "атари" - японское. Но нет - компания американская.
Вот перечень продуктов ATARI:
модели компьютеров XL / XE / XEGS и игровые приставки 2600 (VCS) / 5200 / 7800 / Lynx; Приставки не совместимы с компьютерами, поэтому у меня интереса никогда не вызывали, хотя для коллекции у меня есть 2600 Jr. которая является поздним вариантом этой приставки и выполнена в дизайне ATARI 7800 (на картинках это третья в первом ряду с радужной полосой)
Первый компьютер Возняка Apple I и уже хиты - Apple II и IIc.
Отлично зарекомендовавший себя в США Commodore 64, потом обновленный 64C и гибридная модель сразу с двумя ЦПУ на борту Commodore 128, для обратной совместимости MOS 6502, а для новой конфигурации Z80.
Я совсем не знаком с этими компьютерами и по сути кроме пары роликов на ютубе (о создании игры Elite например) ничего о них не знаю: BBC: Micro/Master
Nintendo: NES / Famicom
Nintendo SNES
NEC PC Engine/TurboGrafx-16 (все варианты исполнения наверное можно не показывать, их много, рекомендую посмотреть видео на Youtube с подробной историей этой бомбической консоли (часть 1, часть 2, часть 3))
Немного про ЦПУ MOS 6502
Я не стану проводить сравнение MOS Technologies 6502 с Motorola 6800 (не путать с 68000), всё есть в википедии, но озвучу основные характеристики:
один восьмибитный аккумулятор A (речь не про элемент питания, а про регистр, который используется для проведения операций над данными и сохранения результата);
два общих 8-битных регистра X и Y;
16-битный счетчик команд PC (платформа изначально ограничена 64 Кбайт памяти, но позднее, в том числе сегодня, ограничение обходится с помощью переключения 8К страниц в дополнительных банках памяти);
указатель стека 8-бит S;
8-битный регистр состояния P.
Общее число документированных команд - 56, недокументированных - 21. Особенностью использования недокументированных команд может быть непредсказуемость результатов или изменения регистра состояния, так же отсутствие поддержки в боле поздних моделях. Впрочем в интернете много мануалов по этим командам в том числе по особенностям применения.
6502 умеет очень быстро работать с первыми 256 байт ОЗУ, это связано с тем, что нулевая страница памяти $0000-$00FF адресуется одним байтом.
ЦПУ MOS 6502 не производился в неизменном виде, например в NEC TurboGrafx-16 использовался Hudson Soft HuC6280
который был продвинутой версией WDC 65C02, в котором убрали недокументированные команды, ввели дополнительные, которые позволяли выполнять быстрее многие ассемблерные конструкции, что позволило уменьшить код программ от 10 до 15%. Так же процессору добавили режим пониженного питания и избавили от багов 6502. В таком виде ЦПУ использовался в таких устройствах, как упомянутый NEC TurboGrafx-16, так и в Apple IIc/IIe, BBC Master, Laser 128 (клон Apple II), ATARI Lynx.
Ну и вставлю из русской вики заметку о варианте советского ЦПУ 4.К602ВМ1 на основе 6502.
"Советский вариант микропроцессора 65С02 был разработан в конце 1980-х годов в московском Научно-исследовательском центре физики и технологии НИЦФТ (НПО Физика). В качестве основы был использован микропроцессор R65C02P2 фирмы Rockwell. Микропроцессор 4.К602ВМ1 с приёмкой 5 (диапазон рабочей температуры −60…+125 °C) обеспечивал работу на частоте до 1 МГц, с приёмкой ОТК (диапазон температуры 0…+70 °C) — до 2 МГц. Особенностью использованного НИЦФТ технологического процесса была возможность высоковольтного (до 18 В) питания микропроцессора, что обеспечило ему высокий «разгонный» потенциал. В частности, при питании напряжением 15 В была получена устойчивая работа микропроцессора на частоте 5 МГц."
Так же многим будет интересно узнать что на 2023 год компанией WDC представлен процессор W62C02S в FPGA варианте, умеющий работать на частоте до 200 МГц, тогда как оригинальный 6502 работал в диапазонах до 2 МГц. При этом напряжение питания варьируется в зависимости от частоты и может составлять от 1,71 В до 5,25 В. Интересно, что потребляемый ток меняется в диапазоне от 0.15 мА до 1.5 мА. Новый ЦПУ планируется к использования в разных SoC вариантах, встраиваемых системах и устройствах для преобразования сигналов. В общем - дедушка еще может.
О графике
Стоит сказать, что графический чип ATARI для меня хоть и более интересный чем любое его отсутствие как на ZX Spectrum, но к несчастью ATARI нужно было году в 82-83 перейти на что-то более продвинутое в XE серии, чтобы получить на экране хотя бы 16 одновременных цветов и какое-то количество аппаратных многоцветных спрайтов как на SEGA Master System или NEC PC Engine. Поэтому сделать что-то визуально выдающееся могли далеко не все и многие хиты 70-х переползая с 2600 (VCS) на 65/130 XE почти не менялись визуально, во всяком случае достаточно, чтобы сказать "Вау!".
Безусловно любая игра после 2600 попадающая на XL/XE уже заметно интереснее выглядит. Во-первых более высокое разрешение, во-вторых - ОЗУ 64 Кб давало возможно реализовать какие-то идеи, которые сложно уместить в 4 Кб картриджа.
Для ряда платформ, в первую очередь интересующую меня ATARI XL/XE, для работы с графикой использовали отдельный чип, который можно программировать для достижения тех или иных результатов отображения на экране. Видеочип не был одинаковым для всей линейки продуктов ATARI, например в приставке 7800 больше аппаратных спрайтов, но отсутствуют некоторые режимы. Но в целом можно спокойно ориентироваться на связку ANTIC+GTIA, которые и стояли в большинстве продуктов.
Я не буду касаться ATARI VCS (2600) так как программирование для этой приставки сильно отличается от XL/XE, там 6502 выступает и в роли видеоконтроллера. А сама приставка имеет всего 128 байт ОЗУ. То есть это игровая приставка с ПЗУ в виде картриджей, а не компьютер.
Основные графические режимы
Код на Бейсике (отобразить 256 цветов)
10 REM * DISPLAY ALL 256 COLORS
11 GRAPHICS 9
12 FOR V=0 TO 15:COLOR V
13 FOR N=0 TO 4:PLOT 5*V+N,0:DRAWTO 5*V+N,191:NEXT N
14 NEXT V
15 D=256*PEEK(561)+PEEK(560)
16 FOR N=1 TO 16:READ V:POKE D+V,143:NEXT N
17 FOR N=0 TO 25:READ V:POKE 1664+N,V:NEXT N
18 POKE 512,139:POKE 513,6:POKE 546,128:POKE 547,6
19 POKE 53248,40:POKE 53249,208
20 POKE 53261,255:POKE 53262,255
21 POKE 53266,0:POKE 53267,0
22 POKE 54286,192
23 GOTO 23
24 DATA 16,28,40,52,64,76,88,102,114
25 DATA 126,138,150,162,174,186,198
26 DATA 8,72,169,0,133,203,104,40,76
27 DATA 95,228,8,72,165,203,24,105,16
28 DATA 141,26,208,133,203,104,40,64
Пример хорошо показывает, что у ATARI на экране можно показать одновременно 256 цветов, но при этом есть сильные ограничения на то, как именно это делается. Ниже по тексту будет игра Timeslip, которая, на мой взгляд, является одной из самых продвинутых для ATARI по демонстрации того, как можно использовать особенности программирования многоцветной графики.
Давайте подробно разберём этот код, чтобы стал понятен сам принцип использования аппаратных вкусняшек на ATARI, так как без них, в ряде случаев, сделать что-то интересное не получится.
Итак,
11 GRAPHICS 9
выбирает режим графики с 16 градациями серого цвета и 4-мя точками на пиксел
12 FOR V=0 TO 15:COLOR V
13 FOR N=0 TO 4:PLOT 5V+N,0:DRAWTO 5V+N,191:NEXT N
14 NEXT V
рисуется поле из квадратов разного цвета (пока помним что речь про оттенки серого).
15 D=256*PEEK(561)+PEEK(560)
инициализируется переменная D, в выражении формируется адрес в памяти зависимый от значений по адресам 560 и 561 (старший байт умножается на 256). В карте памяти ATARI узнаём, что это начальный адрес списка отображения - Display List (подробнее об этом расскажу ниже, сейчас просто запомним, что список отображения позволяет управлять тем, как экран формируется на аппаратном уровне).
16 FOR N=1 TO 16:READ V:POKE D+V,143:NEXT N
тут происходит считывание 16 значений из полей DATA и запись в память для настройки списка отображения.
17 FOR N=0 TO 25:READ V:POKE 1664+N,V:NEXT N
считываются остальные 26 значений из DATA и записываются в память по адресу начиная от 1664, это первый байт второй половины свободной страницы памяти для подпрограмм пользователя.
18 POKE 512,139:POKE 513,6:POKE 546,128:POKE 547,6
для ячеек памяти 512-513 настраивается указатель на новый список отображения, 6 в старшем байте даёт 1536 и прибавляем младший байт 139 - получаем адрес 1675.В этой же строке записываем в ячейки памяти 546-547 значение 6*256+128 и получаем тот самый адрес 1664 из прошлой строки, так как это указатель на пользовательскую процедуру обработки прерывания VBLANK (обратный ход луча в ЭЛТ мониторах и ТВ).
19 POKE 53248,40:POKE 53249,208
20 POKE 53261,255:POKE 53262,255
21 POKE 53266,0:POKE 53267,0
22 POKE 54286,192
В строках 19-22 указывается положение и цвет на экране двух аппаратных спрайтов чтобы создать две черные сплошные границы, которые спрячут небольшие артефакты по выводу изображения, можете эту строку убрать заменив записываемые значения на 0 (в строке 19), спрайты окажутся вне границ экрана и станут видны "дефекты" отображения.
Это код на ассемблере, который мы загрузили по адресу 1664 ($0680):
0680 08 PHP ; сохранить регистры
0681 48 PHA ; в стеке
0682 A9 00 LDA #$00 ; записать 0 в аккумулятор
0684 85 CB STA $CB ; сохранить значение аккумулятора по адресу $CB (203)
0686 68 PLA ; восстановить регистры
0687 28 PLP ; из стека
0688 4C 5F E4 JMP $E45F ; перейти по адресу $E45F (256*228+95=58463)
; а здесь как раз адрес 1675 из строки 18
068B 08 PHP ; сохранить регистры
068C 48 PHA ; в стеке
068D A5 CB LDA $CB ; записать в аккумулятор значение $CB (203)
068F 18 CLC ; сбросить регистр состояний
0690 69 10 ADC #$10 ; прибавить 16 к числу в аккумуляторе (помним что там $CB)
0692 8D 1A D0 STA $D01A ; записать значение аккумулятора по адресу $D01A (256*208+26=53274)
0695 85 CB STA $CB ; записать значение аккумулятора по адресу $CB (203)
0697 68 PLA ; восстановить регистры
0698 28 PLP ; из стека
0699 40 RTI ; вернуться из процедуры прерывания
Адрес 203 находится в самой первой странице памяти и используется в качестве дополнительных ячеек памяти для временных переменных, доступ к этой странице у ЦПУ быстрее чем к остальной памяти. Как мы уже знаем блок кода по адресам $0680-$0688 это пользовательская процедура обработки прерывания VBLANK. То есть значение по адресу $CB (203) обнуляется каждый раз, когда нарисован экран и луч возвращается в левый верхний угол. После обнуления совершается прыжок к коду по адресу $E45F (58463), там находится системный обработчик VBLANK. А вот по адресу $D01A (53274) находится регистр цвета фона. Мы меняем это значение на 16 совмещая регистры цвета пикселей, которые определяют только яркость (оттенки серого) и получаем 16 разных оттенков одного цвета. За один кадр мы меняем значения построчно для отдельных блоков и создаётся визуальный эффект наличия на экране 256 оттенков разных цветов.
Кстати в правой части экрана на границах цветов будет заметен артефакт с тонкой полосой, которая то пропадает, то появляется снова.
Скриншот выше можно сравнить с теми, что внизу и увидеть лишние данные слева и справа. В строке 20 указывается шаблон заливки для аппаратных спрайтов, значение 255 формирует сплошную вертикальную заливку, можете поставить другие числа и посмотреть на результат. 21 строка позволяет указать цвет спрайтов - 0 это чёрный. В строке 22 включаются биты для активации прерываний VBLANK и DLI (по сути мы говорим видеочипу какой код выполнять при обратном ходе луча и как настроить отображение экрана)
На этом примере хорошо видно, что работа с цветом у ATARI привязана к строкам развёртки и ограничение в 4 цвета привязано именно к строкам. А для использования бОльшего числа цветов нужно вдумчиво проектировать игровое поле, как например в игре Pastfinder (смотрите ниже по тексту) или Timeslip.
У ATARI использовалась аппаратная палитра в 256 цветов (часто говорят о 128 для NTSC, из-за того, что соседние цвета идут парами и трудноразличимы, а в одном немецком видео упоминалось, что видеочип игнорирует один бит цвета. Не углублялся в этот вопрос и как показывает практика такая богатая палитра в большинстве игр всё равно ни в каком виде не задействована), из которых в разных режимах можно использовать от 1 до 16, причем в большинстве режимов цвет индексный, то есть в памяти есть
несколько регистров цвета, меняя значение которых - вы меняете цвет у всех пикселей на экране с этим индексом сразу. Отсюда очевидное ограничение платформы в 4 одновременных цвета на экране (есть режим с 5 цветами, о нём ниже) и эффект сдвоенного пиксела, то есть индекс задается двумя соседними пикселами - 00, 01, 10, 11. В случае с бОльшим количеством цветов код цвета представлен 4 битами на пиксел явно.
Код на Бейсике (работа с цветовыми регистрами)
5 GRAPHICS 7+16
10 FOR C=0 TO 3:SETCOLOR C,15,10:NEXT C
30 FOR C=1 TO 3:COLOR C
31 FOR Y=0 TO 9:PLOT C*10,Y
34 DRAWTO C*10+9,Y:NEXT Y
35 NEXT C
36 FOR C=0 TO 4
39 FOR P=0 TO 5
40 FOR I=0 TO 15:SETCOLOR C,I,10
41 FOR J=0 TO 9:NEXT J:REM PAUSE
42 NEXT I:NEXT P:NEXT C
46 GOTO 5
Код на Бейсике (анимация с цветовыми регистрами)
10 GRAPHICS 10
20 REM LOAD COLOR REGISTERS
30 POKE 704,0:POKE 705,82
40 POKE 706,116:POKE 707,196
50 POKE 708,54:POKE 709,68
60 POKE 710,24:POKE 711,102
70 POKE 712,34
80 FOR K=0 TO 19
90 FOR I=0 TO 8
100 COLOR I
110 PLOT 10+K,(40-K*2)+I*10
120 DRAWTO 10+K,(50-K*2)+I*10
130 NEXT I:NEXT K
150 FOR K=0 TO 29
160 FOR I=0 TO 8
170 COLOR I
180 PLOT 30+K,(K*2)+I*10
190 DRAWTO 30+K,(10+K*2)+I*10
200 NEXT I:NEXT K
210 FOR K=0 TO 19
220 FOR I=1 TO 8
230 COLOR I
240 PLOT 60-K,(60+K*2)+I*10
250 DRAWTO 60-K,(70+K*2)+I*10
260 NEXT I:NEXT K
270 FOR K=0 TO 19
280 FOR I=1 TO 8
290 COLOR I
300 PLOT 40-K,(100-K*2)+I*10
310 DRAWTO 40-K,(110-K*2)+I*10
320 NEXT I:NEXT K
325 FOR DE=1 TO 200:NEXT DE
330 POKE 713,PEEK(705)
340 FOR I=0 TO 7
350 POKE 705+I,PEEK(706+I)
360 NEXT I
370 FOR DE=1 TO 15:NEXT DE
380 GOTO 325
Анимация реализована в цикле в строках 325-380.
Максимальное разрешение в монохромном режиме - 320х192 и он совпадает по сигналу с текстовым режимом 40х24 символов (8 бит на знакоместо по 8 горизонтали и 8 байл на знакоместо по вертикали, итого 8х40+8х24). То есть те же 240 строк сканирования и один бит на цвет. Тут нужно объяснить, что монохромность не означает только два цвета черный и белый (тут скорее вопрос в том, что одновременных цветов у пикселей изображения может быть только два - цвет точки и цвет фона), так как есть цветовые регистры для цвета фона символов и при изменении цвета фона мы выбираем цвет из палитры, а при изменении цвета символа мы меняем только яркость в рамках палитры цвета фона. Например стартовый экран Бейсика имеет голубой фон и светло-голубые буквы:
Меняя регистр цвета символов например на значение 50 мы получаем темно-синие символы
мы не можем сделать цвет символов например коричневым сохранив синий фон.
На картинке выше я изменил регистр цвета фона на коричневый, регистр символов так и остался со значением 50, только теперь он не тёмно-синий, а тёмно-коричневый, то есть сохранилась яркость символа, а вот цвет зависим от фона.
Самый контрастный режим - это белый фон и черный цвет символов.
Может показаться что самый яркий и белый цвет будет 255, но смотря на палитру легко заметить, что в значении 255 (правый нижний угол палитры, что показана выше по тексту) у нас находится жёлтый цвет, а белым будет значение 15 - правый верхний угол палитры. То есть цвет никак не связан со значениями RGB в 24-битных системах.
Если же использовать черный цвет для фона и белый для символов, то в эмуляторе вы конечно сможете получить достаточно четкий контрастный экран (так как эмулятор не ограничен аппаратными возможностями старого железа и формировать картинку может как угодно), но на ЭЛТ мониторах буквы будет блёклые, а в некоторых случаях с окрашиванием например в розовый. Подробнее можно почитать здесь.
Ниже на скриншоте пример с игрой Timeslip с включенными в эмуляторе опциями (левая часть), для моделирования картинки, похожей на ЭЛТ монитор, а справа то как это выглядит на эмуляторе в максимально "чистом" от помех виде. Я не люблю эффект scanlines - чем чище, тем лучше, но безусловно теряется аутентичность.
В большинстве цветных игр на ATARI используется разрешение 160х192 (сдвоенная пара бит для одного пиксела), из-за чего эффект сплюснутого пиксела в некоторых играх заметен.
На ЭЛТ телевизорах такие размеры пиксела еще и позволяли бороться с радужным окрашиваем видеосигнала, о котором я упомянул ранее. По этой же причине в псевдографике ATARI символы в областях с вертикальными полосками используют по два пиксела за раз, чтобы символ выглядел отчетливым (в этом например отличие от Commodore VIC-20 и ZX Spectrum где ниже разрешение по вертикали и на один пиксел приходится большее пространство экрана).
Так как отдельной видеопамяти чаще всего не было, то видеобуфер располагался в ОЗУ и чем больше разрешение вы хотите, тем больше ОЗУ вам придется отдать. Для ATARI в максимуме это почти 8 Кбайт (40 байт по горизонтали на 192 байта по вертикали или 40х192=7680 байт). Но из-за возможности менять графические режимы и из-за особенностей многих игр того времени востребованными так же были текстовые режимы, где изображение формируется путём перепрограммирования стандартной таблицы символов - то есть знакоместо 8х8 бит выступает в качестве спрайта.
Код на Бейсике (таблица из знакомест в разных режимах графики)
10 GRAPHICS 0
20 FOR X=0 TO 15
30 FOR Y=0 TO 15
40 COLOR X+1
50 PLOT X,Y
60 NEXT Y
70 NEXT X
80 GOTO 80
Если в строке 10 написан графический режим 0, то вы получите картинку справа, а если замените на 12, то картинку слева. Пусть вас не смущает оператор COLOR, для текстовых режимов он передает код символа, а не цвета, который будет выводиться по указанным координатам оператором PLOT. По сути это альтернатива операторам POSITION и PRINT.
Из бонусов такого подхода - смена таблицы символов в ОЗУ происходит изменением указателя на начало таблицы, по сути - мгновенно, при этом видеочип уже со следующего кадра будет выводить на экран новый символ. Для разработчика это возможность показывать например анимацию персонажа или монстра без существенных нагрузок на аппаратуру и код. Если игра очень простая и занимает мало места в ОЗУ, то проще будет запрограммировать несколько таблиц символов и переключать их например для разных экранов - таблица с очками, главная заставка, игровое поле. Из недостатков - каждая таблица символов занимает 8х128=1024 байт ОЗУ. Но анимацию можно делать и заменой байт в самой таблице, то есть перепрограммировать её на лету, тут каждый разработчик сам принимает решение какой именно подход использовать. В любом случае какой бы способ вы не применяли вы всегда будете сталкиваться с ограничением по памяти. Поэтому некоторые игры и программы использовали дискету в качестве способа расширения памяти и подгрузки при изменении игрового процесса.
Из недостатков платформы именно ATARI XL/XE я бы выделил отсутствие текстового режима с максимальным разрешением 320х192, но с битами цвета под каждое знакоместо, как это реализовано в Commodore 64 или ZX Spectrum. Учитывая хорошую доступную палитру это позволило бы создавать игры в высоком разрешении с использованием бОльшего числа цветов и весьма экономным использованием памяти. Лично мне кажется такой режим был бы куда более востребованным чем добавленные позднее режимы GTIA (в таблице разрешений три последних режима).
Из примера с палитрой мы уже знаем, что видеочип управляется набором команд, которые хранятся в ОЗУ в так называемом "списке отображения" (Display List / DL). Путем замены команд, мы можем указывать видеочипу - как именно нужно поступать с той или иной строкой на экране. Всего в списке указываются параметры для 240 строк видеосигнала, как мы видим это больше чем 192, но всё что больше этого значения может вызывать проблемы с отображением и совместимостью. Но иногда использование было оправдано. С другой стороны размеры экрана напрямую влияют на производительность игры, ведь программисту нужно укладываться в 50/60 кадров в секунду.
Вот посмотрите заставку к игре Dark Chambers.
Если посчитать цвета (везде черный цвет фоновый), то получается 11 цветов на экране. Справа на картинке белыми пунктирными линиями я условно разделил зоны, где меняются регистры цвета по ходу отрисовки экрана. На статичной картинке не очевидно, но так же для факелов и глаз из темноты применяются спрайты PMG (об этом ниже).
А вот так выглядит заставка с анимацией, считаю её отличным примеров реализации качественной заставки с учётом ограничений платформы, хотя надписи сделаны не так впечатляюще, но это дело вкуса.
А ниже вы видите игру Timeslip:
Считать руками я не стал, а софт говорит, что здесь 48 цветов и если во всех трёх игровых зонах хорошо просматривается построчное изменение цвета, то у меня бОльший интерес вызывают текстовые поля с вертикальным разделением - 4 строки с четырьмя разными цветами. Я не изучал код, возможно сделаю это позже, а пока предположу, что это не окрашивание сменой фона у текста, а использование спрайтов PMG. Во всяком случае технически показать в 4-х строках по 4 спрайта можно (хоть вы и имеете только 4 спрайта, а нужно их 16 штук), если вы будете отлавливать отрисовку построчно, то вы можете менять положение спрайтов. Ниже небольшой пример, как можно реализовать в одной строке похожий эффект. Но под вопросом вот что - один спрайт PMG может перекрывать 8 знакомест, а в игре в самой первой строке желтое поле с текстом SCORE занимает 14 знакомест.
Код на Бейсике (использование нескольких цветов в одной строке)
5 GRAPHICS 0
7 POKE 709,255
8 POKE 704,27:POKE 705,57:POKE 706,90:POKE 707,196
9 POKE 53261,255:POKE 53262,255:POKE 53263,255:POKE 53264,255
10 POKE 106,PEEK(106)-8
11 POKE 53248,56:POKE 53249,88:POKE 53250,120:POKE 53251,152
20 POKE 559,62:POKE 53256,3:POKE 53257,3:POKE 53258,3:POKE 53259,3
30 MEM=PEEK(106)-8
40 POKE 54279,MEM:POKE 53277,3:P0=MEM*256+1024
45 P1=MEM*256+1280
46 P2=MEM*256+1536
47 P3=MEM*256+1792
50 FOR L=96 TO 103
60 POKE P0+L,255
65 POKE P1+L,255
70 POKE P2+L,255
75 POKE P3+L,255
90 NEXT L
98 POKE 623,1
99 POSITION 2,8
100 ? "ORBS:12 SC:00000ZT:12-00 SELECT"
Результатом будет вот такой экран
По сути мы взяли 4 аппаратных спрайта PMG и разместили их в определенных местах создав иллюзию цветного фона в текстовом режиме.
В 80-е я сломал голову почему в играх все так плавно и быстро, а попиксельная перерисовка так дико тормозит. Как я узнал заметно позднее - видеочип позволят делать сдвиг изображения аппаратно, это скорее манипуляция с видеосигналом, программируемая по желанию разработчика. Скроллинг использовался очень активно в таких играх как - River Raid, BlueMax, Panther, Pathfinder, Schreckenstein, Timeslip и др. Причем скроллинг может быть разным для разных строк и одновременным как по горизонтали так и по диагонали.
В ATARI, к несчастью, весьма простые одноцветный четыре аппаратных спрайта (по сути это то, что требовалось от видеочипа в конце 70-х, но в таком виде ATARI дожил до модели XEGS 1987 года, при этом XEGS вышел после 7800, где использовался чип MARIA с бОльшим количеством цветных спрайтов чем компьютерный вариант в XE) и два мяча/пули или ракеты, если переводить дословно (всё вместе называется Player Missile Graphics или PMG, в ОЗУ вся конструкция отнимает 2 Кбайт, но если вы не планируете ими пользоваться, то память не будет жёстко зафиксирована под спрайты и вы сможете её использовать по своему усмотрению).
PMG работает независимо от видеорежимов и имеет собственные настройки влияющие на отображение - например может меняться горизонтальная ширина пиксела, выбор слоя наложения (за картинкой или поверх неё) и свои цветовые регистры.
Код на Бейсике (вывод спрайта PMG и его перемещение)
20 GRAPHICS 8
30 POKE 559,62:POKE 53248,120
50 POKE 704,88
60 I=PEEK(106)-8
70 POKE 54279,I:POKE 53277,3
90 REM 53256,3
100 J=I*256+1024
110 FOR Y=J+120 TO J+135
120 READ Z:POKE Y,Z
140 NEXT Y
150 FOR X=0 TO 255
153 POKE 53248,X
154 FOR P=0 TO 16:NEXT P:REM PAUSE
156 NEXT X
160 GOTO 150
600 DATA 126,126
610 DATA 129,129,165,165,129,129
620 DATA 165,165,153,153,129,129
630 DATA 126,126
Код позволяет создать спрайт в виде смайлика и прогнать его по экрану слева направо, как на анимации ниже.
Для формирования цветного персонажа потребуется совмещение нескольких спрайтов как в игре Schreckenstein выше, что ограничивает нас при проектировании игры (да-да, в 70-е одноцветный спрайт был нормой, поэтому многие игры отображают активные элементы в одноцветном режиме). Но все равно мы получаем аппаратное наложение слоёв и детектирование столкновений. Перемещение по оси X (горизонталь) происходит изменением одного регистра в памяти (в коде на Бейсике строка 153), а вот для перемещение по вертикали придётся копировать данные.
Интересно было узнать из интервью одного из разработчиков игры Loop Hero, что они стилизуя игру по 8-битные платформы выбрали одноцветные спрайты для персонажей на карте и это прям стопроцентное попадание в 70-е.
Если следить за ходом луча (не забываем, что это чип, который работает с ЭЛТ экраном) и программировать позицию спрайта и/или менять данные составляющие изображение спрайта, то можно за один проход луча экрана отрисовать одним спрайтом несколько элементов экрана, но!!!! в разных строках. Например в игре Pitfall как раз используется этот прием для рисования подвижных лиан, где для всей лианы использовали один спрайт PMG (квадратик 2х4 пиксела), путём сдвига спрайта вниз вслед за ходом луча ЭЛТ и сменой позиции по горизонтали мы получаем эффект с отрисовкой лианы.
Итого, при перепрограммировании таблицы символов, задействовании максимального (по памяти) видеорежима и использовании PMG - вы от ОЗУ сразу забираете 11 Кбайт. На практике большинство проектов можно реализовать в текстовом режиме 20х24, с поддержкой 5 цветов с перепрограммированием таблиц символов и с PMG. Это чуть меньше 2.5 Кбайт.
Теперь о пяти цветах. С одной стороны кажется, что имея 2 бита на пиксел можно закодировать 4 цвета, но при использовании текстового режима раскрашивание пикселей происходит по номеру символа в таблице, а в ATARI таблица всего из 128 символов (7 бит). Остальные 128 символов (или по-другому - код символа со значащим последним битом) представляют собой те же 128 символов, но с инверсией. Когда же вы выводите на экран в цветном режиме код инвертированного символа, то один из цветов окрашивается значением из другого регистра. Вот и вся магия. Сам символ берётся из той же таблицы, манипуляция происходит только с регистром одного из цветов.
Код на Бейсике (вывод 5 цветов)
10 GRAPHICS 12
20 FOR X=0 TO 15
22 INV=X
24 IF X>7 THEN INV=X-7+128
30 FOR Y=0 TO 15
40 COLOR INV+1
50 PLOT X,Y
60 NEXT Y
70 NEXT X
80 GOTO 80
А этот код позволяет увидеть как получается 5 цветов.
Есть обратная сторона этого режима, так как символов на экране 480 (20x24), а в таблице только 128 значений, то не получится весь экран представить в виде разных спрайтов на основе таблицы символов. Как очевидное решение - повторяющиеся узоры, как например в Starquake.
Разработчики этой игры использовали режим высокого разрешения, что значительного повысило качество спрайтов, чтобы не делать игру черно-белой, они меняли регистры цвета для игровой зоны экрана, а в режиме высокого разрешения цвет фона и пикселя выбирается в одной цветовой гамме, но с разной яркостью (об этом мы говорили выше). На первой сцене ниже фон выбран синий, а видеочип все установленные биты окрашивает в голубой.
В итоге при смене экрана нам меняют цвет. Кстати сам персонаж и его левитирующая платформа - это как раз спрайты PMG, что сильно упрощает код и разгружает процессор.
Использование спрайтов PMG убирает необходимость в обработке наложений изображений. Для примера вот видео для версий игры для ATARI, C64 и DOS (на мой взгляд версии C64 и DOS смотрятся для игры выигрышнее, казалось бы, чего стоило разработчикам ATARI добавить к видеочипу еще один графический режим вместо режима с 16 оттенками серого, которые просто нигде не использовались. При 16 одновременных цветах на экране, потребовалось бы всего 960 байт для этого режима на цвета фона и знакоместа и еще 960 на сами символы. А если разгуляться фантазии то можно было бы с помощью регистра задавать настройки режима, например 16 цветов или 8 цветов и бит мерцания).
Версия Starquake для DOS один из случаев где палитра CGA не раздражает. Разрешение такое же как на ATARI, но отдельные элементы игры имеют свой цвет. Анимация и геймплей соответствуют другим платформам.
Игра Starquake на разных платформах
Позднее, уже в 2000-х, команды энтузиастов и любителей демо-сцены, стали активнее реализовывать потенциал видеочипа ATARI, что привело к появлению комбинаций разных режимов, в том числе к статичным программам, которые при запуске рисуют на экране картинку в каком-либо нестандартном виде, чаще всего с бОльшим числом цветов. На ютубе есть видео, где лектор показывает как можно добиться на экране до 1000 цветов. К несчастью я не имею исходников, а лектор только говорит в общих чертах о механизме реализации.
Конечно же на ATARI применимы и все известные способы смешения цветов за счёт соседних пикселей - дизеринг (Dithering).Лучше всего конечно такие приемы проявляют себя на ЭЛТ экранах, всё же пиксельная четкость LCD панелей не даёт возможности получить такой же эффект.
Уже в наши дни появился софт для конвертации полноцветных изображений Rasta Converter, и инструменты для создания исполнимого файла XEX, который формирует на экране изображение. Зачем такие сложности с кодом? Потому что сама программа является кодом управления видеочипом, который перепрограммируется на лету для достижения большего одновременного числа цветов на экране (упрощенный пример того как это делается мы рассмотрели ранее, когда выводили палитру в 256 цветов).
Ниже несколько примеров, посмотреть их можно в эмуляторе Altirra. Для формирования изображения используют вместе все особенности программирования видеочипа, интересной особенностью будет использование спрайтов PMG для формирования слоя с дополнительным цветом, это позволяет выводить до 9 цветов на строку. С одной стороны можно спрайт PMG залить сплошным цветом и получить цветной прямоугольник 64х192, так как их четыре, то мы можем перекрыть площадь размером в 256х192, но кроме такого подхода мы можем следить за ходом луча и в каждой строке менять положение спрайтов и их цвет.
Специальные программы выводящие картинки с применением трюков программирования видеочипа ATARI
Стоит отметить, что данные примеры при запуске на оригинальном железе лучше выглядят на ЭЛТ мониторе или телевизоре, нежели на современных мониторах. Это связано с особенностью сглаживания соседних пикселей. Как здесь - уменьшенные в размерах картинки позволяют так же сгладить переходы и уменьшить отрицательный эффект от низкого разрешения. Так же не забываем что это реализовано на железе 70-80-х.
Но весь фокус в том, что вытягивать такую графику из видеочипа ATARI приловчились только после 2000-х годов. Примеры взяты из проекта RastaConverter. Интересно, что если бы я такие картинки увидел в 80-е, то даже не понял бы всю специфику и сложность по формированию этих изображений и принимал бы всё как само собой разумеющееся.
Заключение
Я постарался, не слишком погружаясь в детали, показать особенности работы компьютера ATARI и того, как достигались те или иные решения в графике. По ходу написания статьи и особенно погружаясь в небольшое исследование игры Starquake - мне захотелось написать подробную статью об играх ATARI, во всяком случае о тех, которые на мой взгляд заслуживают внимания. Сейчас волна ретро-гейминга охватила всех геймеров и возможно такой материал поможет понять, какие из 2-3 тысяч игр достойны внимания. Платформа охватывает как игры 70-х, так и 80-х годов и в визуальном плане они сильно различаются, от классических Pacman и Space Invaders, до Timeslip, Starquake, Karateka или недавнего релиза Prince of Persia - для платформы ATARI эта игра не выходила в конце 80-х.
Принц Персии работает на стоковом железе, но с 128К памяти на борту, то есть фактически вам потребуется или 130XE или любая машина с модом на оперативку. Игра, на мой взгляд, хорошо показывает возможности платформы.
Спасибо дочитавшим до конца!