Эта статья часть серии про порты игры Another World. В ней пойдёт речь про хитрости при работе с Amiga 500. Рекомендуется сначала прочитать предыдущую статью.
История Amiga начинается в середине 1982 года со звонка Ларри Каплана Джею Майнеру. Оба тогда работали в Atari в 70-х. Оба решили покинуть компанию. Каплан из-за отсутствия признания и Майнер из-за запрета руководства на использование крутого, но дорогого процессора Motorola 68000.
Покинув Атари, Каплан основал Activision. После того, как инвесторы обратились к нему с просьбой разработать новую игровую платформу, он связался с самыми блестящими людьми, которых он знал. Майнер взялся за аппаратную часть вопроса в только что образованной компании «Hi-Toro». Система получила кодовое название «Lorraine».
К концу 1983 прототип был собран. Устройство впечатлило посетителей Consumer Electronics Show (CES) в январе 1984 года, благодаря демке Boing Ball, в которой огромные спрайты перемещались с фреймрейтом 60 кадров в секунду. Устройство анонсировали в 1985 году под именем «Amiga from Commodore», позже переименовав в Amiga 1000.
На заметку: Amiga 1000 не могла загрузиться сама по себе, у устройства не было ПЗУ. Загрузчик был на дискете, и лучше бы вам хранить её как зеницу ока!
Серия статей
- Полигоны Another World.
- Полигоны Another World: Amiga 500.
- Полигоны Another World: Atari ST.
- Полигоны Another World: IBM PC.
- Полигоны Another World: Sega Genesis.
A500
К 1985 году, после серии ошибок, Commodore оказался в тревожной ситуации на грани банкротства. Томас Раттиган, тогдашний главный операционный директор, провёл радикальные изменения. Помимо амбициозного плана, охватывающего почти все подразделения компании, он разделил Amiga 1000 на два продукта: новая high-end версия, предназначенная для творческого рынка под названием Amiga 2000 и недорогая версия для Commodore 64 под названием Amiga 500.
Amiga 500, также известная как A500, была выпущена в 1987 году. Под капотом Motorola 68000, работало устройство на 7,16 МГц и имело 512 КБ ОЗУ. Устройство стало чрезвычайно успешным, оно снискало популярность среди игроков, программистов, в частности, людей из демо-сцены. Это был самый продаваемый продукт Commodore (было продано примерно 6 миллионов единиц с 1987 по 1991 год[1]).
В A500 был ПЗУ, но памяти было достаточно лишь на размещение загрузчика под названием Kickstart. После инициализации оборудования Kickstart предлагает пользователю вставить дискету, содержащую либо программу, либо ОС Workbench. Дискета должна была оставаться в приводе, пока машина включена. Более поздние модели, такие как A1200 (a.k.a величайшая из когда-либо созданных машин), имели пространство для 2,5-дюймового жёсткого диска и были избавлены от использования дискет.
Архитектура
Поскольку технология изначально создавались с расчётом на гейминг[2][3], Amiga не была построена на «железном» процессоре с возможностью аудио и видео, как большинстве компьютеров того времени. 32/16-битная 68000 работает вместе с чипсетом, на котором размещено три мощных чипа: Paula (аудио), Denise (видео) и Agnus (манипулирование данными и синхронизация).
Подобный дизайн с системой памяти, обеспечивающей не только плоскую адресацию, но и общую оперативную память, доступную как для процессора, так и для чипсета, очень способствовал популярности Amiga среди разработчиков. Для сравнения, ни Sega Genesis, ни Nintendo SNES, две мощные системы, выпущенные спустя годы (соответственно, в январе 1989 г. и ноябре 1990 г.), не имели общей памяти.
Шина чипсета имеет сложную систему приоритетов, в которой 68000 не активен на чётных циклах. DMA (прямой доступ к памяти) чипсета пытается использовать только нечётные циклы для постепенного мультиплексирования доступа к шине без влияния на процессор. Но не всегда всё было гладко. Agnus, в частности, вполне справлялся там, где 68000 голодал[4].
Для решения этой проблемы клиенты могли приобрести расширение «Fast RAM» с выделенной «шиной ЦП» для 68000. С размещением там инструкций 68000[5] ЦП не голодал, когда канал DMA блиттера[15] был активен. Это удваивало скорость исполнения 68000.
Видеосистема
Видеосистема полностью управляется Denise, предлагая в общей сложности двадцать графических режимов[6]. Самое популярное разрешение было 320x200 с соотношением сторон 1,6, что не соответствовало мониторам того времени (4/3 = 1,3). Соотношение сторон приводит к искажениям, когда кадровый буфер передается на ЭЛТ монитор.
Фреймбуфер хранится не непрерывно, а в отдельных областях памяти, называемых битовыми плоскостями. Может быть выделено до пяти[7] битовых плоскостей по 8 КБ, что даёт 5 бит на пиксель, позволяя получить 32 цветовых индекса. На первый взгляд этот подход выглядит весьма неуклюжим (особенно для разработчика с опытом работы с ПК), но Agnus и особенно его блиттер в целом обеспечивают большую ясность.
Палитра основана на 4-битном RGB цветовом пространстве на канал. 12-бит на цвет позволяет определить до 4096 различных цветов, что было намного больше, чем обычно на компьютерах той же эпохи.
Многие трюки позволили отобразить больше цветов. Как Copper, позволяющий изменить палитру на HSYNC.
Следующие два изображения представляют две противоположные стороны цветового пространства RGB. С чёрным в координатах (0x0, 0x0, 0x0), красным в (0xF, 0x0, 0x0), зелёным в (0x0, 0xF, 0x0) синим в (0x0, 0x0, 0xF) и белым в (0xF, 0xF) 0xF). Эти красочные изображения хорошо иллюстрируют творческую свободу, предоставленную разработчикам графики.
Another World на Amiga
Another World на Amiga, собственно говоря, не является портом. Поскольку A500 использовался для разработки, это оригинальная версия, созданная в период с 1989 по 1991 год 21-летним Эриком Шайи, работавшим в одиночку в своей спальне.
Две причины сделали Amiga идеальной машиной для разработки. Во-первых, GenLock позволил сделать ротоскопирование. Во-вторых, и что наиболее важно, Amiga Agnus чрезвычайно облегчил рендеринг полигонов.
Блиттер
Идея создать игру, основанную исключительно на полигонах, возникла из-за неправильного предположения о том, что «Dragon's Lair, Escape from Singe's Castle» на Амиге использовала их[8]. Эрику нужно было как-то это реализовать с разумной частотой кадров. Именно на этом этапе исследований и разработок блиттер Амиги сыграл ключевую роль.
Отрисовка полигонов
В документации к блиттеру упоминается функция под названием «Area Fill Mode». Мы не говорим о причудливой трехмёрной проекции или текстурировании. Блиттер работает в экранном пространстве на битовых строках с возможностью «fill the blank». Работа базируется на сканированию слева направо. Пока блиттер видит 0, ничего не происходит. Как только первая 1 пройдена, блиттер заполнит строку единицами до следующей 1. Рисунок из документации хорошо иллюстрирует работу. Обратите внимание, что даже вогнутый многоугольник может быть правильно отображён с помощью этого метода.
bit array before bit array after ______________________ ______________________ | | | | | | | | | | | | | 1 1 1 1 | | 11111 11111 | | 1 1 1 1 | | 1111 1111 | | 1 1 1 1 | | 111 111 | | 11 11 | | 11 11 | | 1 1 1 1 | | 111 111 | | 1 1 1 1 | | 1111 1111 | | 1 1 1 1 | | 11111 11111 | | | | | | | | | |______________________| |______________________|
Подобное решение приводит ко второй проблеме — как нарисовать «границы» многоугольника. Если вы внимательно посмотрите на рисунок выше, вы увидите, что это нестандартный алгоритм Брезенхэма[9], поскольку горизонтальные линии должны быть пропущены. К счастью, дизайнер Амиги подарил блиттеру режим «line draw»[10].
Мы ещё не закончили. Есть ещё проблемы. Во-первых, «рисование линий и затем заполнение области» должно быть выполнено четыре раза (один раз для каждой битовой плоскости), что кажется очень дорогостоящим. Во-вторых, движок должен рендерить сотни полигонов. Блиттер нуждается в чистом буфере, полном нулей и хороших границ из единиц для работы. После нескольких полигонов фрейм-буфер наверняка будет похож на суп из битов. Наконец, блиттер выводит только 1-цы, но нам нужна возможность выводить 0 в некоторых из четырёх битовых плоскостей, чтобы генерировать правильный 4-битный цвет.
Решением этих проблем является настройка входов A, B и C блиттера[11]. Этот процесс лучше объяснил Blogger Scali в своём блоге[12].
… есть решение этой проблемы, и это даже не так сложно. Блиттер может рендерить где угодно в chipmem, поэтому легко установить временный чистый буфер, «scratchpad», и отрисовать в нём многоугольник. Затем вы копируете его в фактическую область экрана, используя бит блит по маске[16]. Часто эту операцию ещё называют «cookie cut». Это, в сущности, та же самая операция, которую вы использовали бы с 2D-изображениями, когда вы записываете пиксели, только когда они установлены в исходном изображении, и оставляете целевые пиксели нетронутыми в противном случае (логическая операция ИЛИ). Это правильно объединит полигоны на экране.
На самом деле, возможно, сейчас самое время объяснить блиттер более подробно. Блиттер имеет 3 входа и 1 выход. Все они обрабатываются DMA, поэтому он может работать полностью независимо от ЦП после настройки. 3 входа могут быть объединены с помощью логических операций. Результат затем записывается в выходной канал. В случае блита по маске вы, в общем-то, выполняете такую операцию:
выход = (маска И растровое изображение) ИЛИ (НЕ маска И вывод)
— Scali's OpenBlog
С учётом входных данных блиттера, у нас теперь есть полная картина того, что требуется для рендеринга каждого полигона.
- Выделить кусок буфера.
- Очистите нулями с помощью блиттера.
- Отрисовать границы многоугольника единицами в режиме «draw line».
- Заполнить область буфера единицами в режиме «area fill».
- Блитнуть кусок буфера четыре раза (один раз для каждой битовой плоскости).
Это было не так весело, но работа сделана. Усилие стоило того, так как программа могла обрабатывать до 50 полигонов (в зависимости от их размера) со скоростью 20 кадров в секунду. Что приводит нас ко второй проблеме.
Копирование фреймбуфера
Как бы быстро блиттер ни мог отрисовать полигоны, он всё ещё был недостаточно быстр. Каждый кадр состоит из тысяч полигонов. Некоторые полигоны настолько малы (1x1, называемые пиксигонами), что не оправдывают накладные расходы. Один из первых фонов в игре (когда Лестер выходит из воды) состоит из 981 полигонов.
Решением было кэшировать фон в специальный BKGD буфер с простым копированием. Это было специальным заданием для блиттера.
Блиттер — один из двух сопроцессоров в Amiga. Являясь частью чипа Agnus, он используется для копирования прямоугольных блоков памяти и отрисовки линий. При копировании памяти он примерно в два раза быстрее 68000 и способен перемещать почти четыре мегабайта в секунду. Он может рисовать линии со скоростью почти миллион пикселей в секунду.
— Amiga Developer CD v2.1 (OS 3.5)
Со скоростью 4000 байтов в миллисекунды, блиттинг BKGD-буфера в начале каждого нового кадра требует «всего» 8 мс.
На заметку: несмотря на то, что Another World была отполированной игрой, некоторые края остались «шероховатыми». Если игрок попытается проявить смекалку во время викторины по защите от копирования[13] и нажмёт «c», чтобы ввести код для перехода к игровому процессу, экран становится зеленым, и Amiga зависает. Единственный выход – рестарт игры.
Заполнение фреймбуфера
Несмотря на то, что процессор 68000 может записывать 16 бит за раз, очистка фреймбуфера также, вероятно, была выполнена с помощью блиттера. Документация Amiga содержит пример кода «clearmem»[14], который использует блиттер для очистки 128 КБ ОЗУ.
Ссылки
- Wikipedia: Amiga 500.
- Commodore: The Amiga Years.
- Commodore: The Final Years.
- Amiga Chip RAM.
- Wikipedia: Amiga Hardware.
- Amiga Screen Modes.
- Amiga Real-time 3D Graphics.
- Classic Game Postmortem — Another World.
- Алгоритм Брезенхэма.
- 6 Blitter Hardware / Area Fill Mode.
- 6 Blitter Hardware / DMA channel.
- Just keeping it real, part 3.
- Code wheel.
- Amiga Documentation, 'Example: Clearmem'.
- Блиттер.
- Bit blit.