[Перевод] Как сделана Atari 2600: извлечь нечто (почти) из ничего

ab6f3a406ad15ae0b3ee15f2693ba6ef.png
Atari Combat, 1977

Atari 2600 не была первой домашней игровой видеоприставкой со сменой игр, однако она первой получила громкий успех. Представленная в 1977 году как Atari VCS (Video Computer System), а в 1982 году переименованная в Atari 2600, она была продана в количестве более 30 миллионов экземпляров и создала новый рынок, который до сих пор удерживают PlayStation и Xbox. До появления 2600-й большинство видеоприставок были либо монетоприёмниками, например в барах, либо устройствами с фиксированными функциями, ограниченными несколькими встроенными играми, такими как Pong. Первая домашняя система Atari стала началом новой эры.

Этот блок электроники в деревянном коробе будоражил мой неокрепший ум. Мне очень хотелось, чтобы у меня была такая приставка, но склонить к этому родителей не удалось. В 1982 году, когда мне было одиннадцать, у моего друга Фреда появилась Atari, и меня одолела зависть [мы закрыли непечатные слова]:

sukwxxadue89j8pz0mbhavqaauu.png
16 декабря 1982 года, день, когда была получена Ataria (sic)
Вот так денёк выдался! Фреду досталась приставка Atari, а мне — зубные брекеты и рубашка с крокодилом Izod Lacoste.

Не так давно после праздников я заинтересовался архитектурой аппаратной части 2600-й и стал читать всё, что находил по этой теме. Я знал, что это была какая-то система на базе 6502-го проца — [это восьмиразрядный микропроцессор, разработанный компанией MOS Technology в 1975 году — прим. перев.], и слышал немного о «гонке с лучом» (racing the beam), но этим тогда мои знания и ограничивались. Я был потрясён, обнаружив, насколько примитивным было оборудование 2600-й даже по сравнению с современными системами на 6502-м, такими как Apple II, Commodore PET и даже 8-разрядные компьютеры Atari.

В деревянном коробе — всего три цифровых «микрухи»:


  • центральный процессор 6507 (версия 6502 с урезанным количеством выводов);
  • I/O-таймер ОЗУ 6532 RIOT;
  • TIA (пользовательская микросхема Atari).

Примечательно, что никаких ОЗУ и ПЗУ в этом списке не значилось. Использовалось ПЗУ любого игрового картриджа, который туда вставлялся, — никаких вспомогательных встроенных процедур ввода-вывода и операционной системы там не было и в помине. Всё это должен был предоставить программист, который писал игру. Игровые картриджи ограничены 4 КБ, а многие ранние игры занимали всего 2 КБ. Любое изображение на этой странице весит куда больше.

e5e4fa9aaf67157d978db178f56d1018.jpg
Печатная плата для ранней версии Atari VCS

Оперативная память ограничивалась крошечным объёмом встроенной памяти микросхемы 6532 RIOT — всего 128 байт. 128 байт! Я… даже не знаю, как сказать насколько это… мало. Очень, очень мало. Я мог бы предположить, что там 1 КБ или 2 КБ ОЗУ, но 128 байт — цифры совсем другого порядка. Хуже того, этот крошечный объём оперативки должен был служить и буфером/массивом, и стеком! На местоположение игрока и игровых объектов, на силу, очки и прочее у программистов было несколько байт — и всё.

Но постойте-ка. Дело обстояло ещё хуже, чем вы думаете. Это 6507 с урезанным числом выводов, и выводы NMI и IRQ 6502 были ликвидированы. Поэтому возможности аппаратного прерывания не было никакой. Всё приходилось выполнять с помощью программной синхронизации и опроса оборудования. Для системы реального времени, построенной на концепции гонки с лучом, это было просто мазохизмом.

И, наконец, удар ниже пояса. Кадрового буфера тоже не было. И даже линейного буфера. В распоряжении программиста всего-то и было, что несколько регистров TIA. Большая часть графики должна была генерироваться центральным процессором на ходу, прямо в тот момент, когда электронный луч телевизора сканировал интересующие пиксели. И даже сигнал VSYNC для телевидения обрабатывался программно. При такой аппаратуре удивлён, что для загрузки игр на Atari 2600 не понадобились угольный паровой котёл и рукоятка с кривошипом! Это безумие. Просто класс! Обожаю!

Сердцем 2600-й была микросхема TIA разработки Atari — TIA расшифровывается как Television Interface Adapter. Если вам интересен принцип работы, в сети можно найти начерченные от руки монтажные схемы TIA [на рисунке — фрагмент монтажной схемы — прим. перев.]. Современному человеку «внутрянка» TIA покажется очень странной. Для начала, повсюду используются сдвиговые регистры с линейной обратной связью — даже там, где мы больше ожидаем увидеть двоичные счётчики, например, для счёта строчных синхроимпульсов регистров учёта позиций спрайтов. Я видел, как регистры сдвига с линейной обратной связью (LFSR) использовались для генерации случайных чисел в других 8-разрядных схемах, но в качестве счётчиков общего назначения они не использовались никогда. Эти LFSR также имели два отдельных таймера со сдвигом по фазе на 180 градусов, что выглядит так же нелепо. А вот 6-разрядный счётчик горизонтальной синхронизации:

e36a397d1f545afc761e3aa6744e93d6.jpg

У разработчиков микросхем наверняка были причины поступить так. Возможно, реализация LFSR обходилась дешевле или требовала меньшего числа транзисторов, чем обычные бинарные счётчики? Если нужен просто шестиразрядный счётчик, по сути дела, не имеет значения, считает ли он 64 состояния последовательно от 000000 до 111111 или же следует какой-либо псевдослучайной детерминированной последовательности состояний. В любом случае для проверки терминального состояния и сброса счётчика при необходимости можно добавить логический контур. Если у кого-то есть мысли о том, зачем конструкторы TIA пользовались для этого LFSR, мне бы очень хотелось это знать. К счастью, программиста Atari 2600 эти забавы с LFSR по большей части не затрагивали.

Как игры в целом отрисовывают изображения? Для простоты начнём с того, что в случае Atari называют игровым полем (playfield). По сути, это фоновое изображение на экране. В TIA есть 20 битов состояния в регистре, которые программист может менять и которые используются для создания одномерного монохромного растрового изображения с низким разрешением в левой половине строки развёртки. Правая половина строки развёртки является либо копией, либо зеркальным отражением левой. Хотите, чтобы справа было что-то совсем другое? Тем хуже для вас. Хотите, чтобы цвета были разными? Тоже проблематично. Одни и те же 20 битов регистра состояний игрового поля используются на каждой строке горизонтальной развёртки. Хотите, чтобы содержимое строки развёртки отличалось друг от друга? Для этого понадобится постоянно менять регистры игрового поля — до отрисовки каждой строки развёртки. У генератора импульсов центрального процессора (CPU clock) всего 76 циклов на каждой линии. При этом большинство команд ЦП занимает от 2 до 5 циклов, что не оставляет нам времени на… в общем-то, практически, ни на что.

Такое поведение игрового поля объясняет, почему на Atari так много игр с зеркально симметричным фоном, стенами и прочим подобным. Посмотрите на этот кадр из игры Pitfall и вы увидите, что кроны и стволы деревьев, земля, яма и подземный ход симметричны. Всё это встроено в игровое поле (с фокусами, о которых я напишу позже). Спрайтами здесь являются только Гарри Питфол, лиана, на которой он висит, катящееся бревно и скорпион.

2d26d8d1c9b4c523f6f7e94feaf3a681.png

И что же это за спрайты? В Atari их называют «игроками» (players) и «ракетами» (missiles), но концепция у них одна. «Игроки» [поскольку NPC обычно мало похожи на боеголовки, а цифры счёта и брёвна, которые отрисовываются с помощью спрайтов «игрока» — на протагониста игры, я ставлю эти термины в кавычки — прим. перев.] — это спрайты шириной в восемь бит с меньшим размером пикселей, чем у игрового поля. Они могут находиться в любом месте строки развёртки, но, как и игровое поле, являются одномерными монохромными растровыми изображениями. Если программисту нужны двухмерные спрайты (а они, конечно, нужны), то код должен постоянно менять регистр графики «игрока» (player graphics register). При этом обновлять его нужно до прорисовки каждой новой строки развёртки, в том числе обнулять регистр под и над спрайтом «игрока», где не нужно ничего отрисовывать. Вам кажется, что это невероятно утомительно? Ещё как!

У «ракет» же вместо восьми бит — всего по одному, в остальном они идентичны игрокам. TIA позволяет использовать двух игроков, две «ракеты» и один «мяч», который, по сути, является третьей «ракетой». Если программисту нужно больше спрайтов, цветные спрайты или что-то ещё, чего аппаратная часть не поддерживает, ему придётся сделать «финт ушами» и либо комбинировать несколько игроков или ракет, либо точно рассчитывать по времени несколько дополнительных обновлений регистров TIA, создавая иллюзию дополнительных цветов или спрайтов.

Один из распространённых приёмов — разработка игр с чётким разграничением горизонтальных полос активности объектов, как в Pitfall. Это позволяло несколько раз использовать один и тот же спрайт игрока по мере отрисовки экрана сверху вниз. В Pitfall можно было сначала использовать «игрока» player 0 для отрисовки числа очков в верхней части экрана. Тот же аппаратный ресурс player 0 затем использовался для отрисовки части Гарри Питфола, катящегося бревна и, наконец, скорпиона. Поскольку ничто из этого не перекрывается в горизонтальной плоскости, конфликта не возникает, пока программное обеспечение быстро обновляет графику и положение «игроков» при переходе от одной строки развёртки к другой.

При такой одномерной аппаратной системе обнаружение столкновений было очень сложной задачей, если бы оно возлагалось на программное обеспечение. Необходимая степень учёта была бы слишком высокой: проверка всех спрайтов и игрового поля на предмет столкновений друг с другом была бы практически невозможной, когда есть всего 76 тактов на строку развёртки, которые сверх того расходуются на все важные задачи процессора. К счастью, в TIA предусмотрена одна очень крутая возможность для аппаратного обнаружения столкновений на уровне пикселей! Каждый раз, когда ненулевой пиксель перекрывает другой ненулевой пиксель игрового поля, «игрока», «ракеты» или «мяча», в TIA выставляется соответствующий бит столкновения, который программное обеспечение может позже проверить и очистить. Если графических объектов 6, TIA нужно отслеживать (6×5)/2 = 15 возможных столкновений (применение задачи о рукопожатиях). Это здорово!

О сложности горизонтального позиционирования «игроков» и «ракет» ходит дурная слава. Большинство программистов могли бы ожидать, что в TIA есть регистры с указанием положения каждого спрайта по горизонтали, но их там нет. Это было бы слишком просто. Горизонтальное положение «игрока» или «ракеты» на Atari 2600 задаётся при помощи записи в специальный регистр TIA в тот самый момент, когда электронный луч проходит требуемое положение. Задумайтесь об этом на минуточку. Безразлично, какое конкретно значение мы запишем в регистр. Программа не может сказать TIA «помести игрока 0 в позиции X». Вместо этого она говорит TIA «помести игрока 0 в… (подождите-ка) прямо здесь!» Благодаря такой конструкции горизонтальное позиционирование требует синхронизации программного цикла с началом строки развёртки, некоторой задержки, зависящей от желаемого положения по горизонтали и дальнейшей записи в регистр TIA. Вместо того чтобы задать конкретное значение горизонтального положения объекта, программа выполняет сброс одного из LFSR в TIA.

При использовании стандартного метода горизонтального позиционирования по времени прохождения луча горизонтальное разрешение можно получить только на пять тактов процессора, что эквивалентно 15 пикселям. Чтобы программист мог точно регулировать положение, в TIA предусмотрены дополнительные регистры, которые позволяют настраивать каждый спрайт в диапазоне от -8 до +7 пикселей от его обычного положения. Это неудобно, но сочетание позиционирования по времени и тонкой регулировки позволяет придавать спрайтам любое горизонтальное положение.

Тонкое управление горизонтальным положением включает запись в регистр TIA под названием HMOVE, и его использование приводит к одному из самых печально известных графических недостатков Atari 2600: неравномерным чёрным линиям в левой части экрана, которые закрывают часть игрового поля. Часто этот эффект называют гребёнкой HMOVE. Вот пример гребёнки из игры Space Invaders:

f8fd37ca85e6f17ecae9847a2009fcae.png

Это побочный эффект способа, которым TIA выполняет точную регулировку положений спрайтов/ Этот способ обнаруживает себя во многих играх. Каждый раз, когда в строке производится запись в регистр HMOVE, горизонтальный пробел в этой строке вырастает на 8 пикселей, обрезая её левый край. Что это, баг? Непредусмотренная особенность? Подробности ответа на этот вопрос слишком сложны, чтобы рассказывать о них здесь, но Эндрю Тауэрс подробно объяснил такое поведение TIA. Его объяснение можно найти здесь. Смотрите заголовок Playing with the HMOVE Registers.

Но почему в одних играх эффект гребёнки HMOVE налицо, а в других, по всей видимости, нет? Эффект проявляется только тогда, когда игры повторно используют один и тот же спрайт в разных вертикальных положениях на экране, что требует регулировки горизонтального положения спрайта в середине кадра. В Space Invaders это делается повсеместно, но в простых играх наподобие Combat такого не происходит. Combat ограничивается двумя встроенными «игроками» и двумя встроенными «ракетами» без изменения положений каких-либо объектов в середине кадра. Поэтому и гребёнка HMOVE там не появляется.

В Pitfall используется другой подход: вместо гребёнки слева сплошная чёрная полоса. Она является результатом записи в HMOVE на каждой строке, даже там, где это не требуется. Activision использовали этот подход во многих играх. Видимо, они заключили, что сплошная чёрная полоса выглядит лучше, чем прерывистая чёрная гребёнка.

Для создания качественной игры на Atari требуется ещё много программных фокусов. Несимметричное или разноцветное игровое поле можно создать путём изменения графических и цветовых регистров игрового поля в нужный момент, но добиться этого очень непросто! Цветовые регистры также могут меняться от строки к строке, чтобы увеличить общее число цветов на экране даже при ограниченном количестве цветов в одной строке. Спрайты могут использоваться повторно и менять положение по вертикали. Они даже могут вторично использоваться в той же строке при условии точного тайминга и внимания к поведению TIA. Программирование под Atari 2600 — тема очень глубокая. Оно прошло долгий путь от демоверсий с прыгающим шариком до такой высококачественной игры, как Pitfall.

Хотите попробовать свои силы в написании демоверсий игр для Atari? Да, вы этого хотите, и сегодня сделать это гораздо проще, чем в 1977 году. Начните с руководства Эндрю Дэвиса Atari 2600 Programming for Newbies. Программное обеспечение пишется на ассемблере 6502. Высока вероятность, что вы с ним знакомы, раз читаете этот блог. Для ассемблирования программ используйте DASM, старый и функциональный кросс-платформенный ассемблер для 6502 и других 8-разрядных ЦП. Если у вас есть настоящая приставка Atari 2600, вы можете записать двоичный образ ассемблированной программы в EPROM и создать собственный игровой картридж. Если же это кажется вам слишком хлопотным, попробуйте программный эмулятор Z26 или Stella.

Может быть, я исказил какие-то технические аспекты или упустил важные детали? Пожалуйста, сообщите мне об этом! Я только начинаю свой путь в мир аппаратной части Atari, и мне ещё многое предстоит узнать. Но уже скоро вы сможете увидеть мою первую игру для 2600-й.


Название — отсылка к седьмой древнекитайской стратегеме, которая традиционно переводится как «извлечь нечто из ничего», автор к этой отсылке и стремился, поэтому и внёс almost в скобки, так как в стратегеме этого слова не было.

cqna880todtt287i6ffb12uzzwk.png

Data Science и Machine Learning

Python, веб-разработка

Мобильная разработка

Java и C#

От основ — в глубину

А также


© Habrahabr.ru