Как работает денди
Денди спроектирована так, что вся программа и все данные хранятся исключительно на картридже. Сама приставка не содержит никакой постоянной памяти. Картридж устанавливается в разъём, на который выведены как шины CPU, так и PPU (см. приложение 4). До сих пор мы, для простоты и наглядности, ссылались на модель картриджа, содержащую 32k PRG-ROM и 8k CHR-ROM без наличия маппера. Такие картриджи подходят для хранения небольших игр - их память полностью адресуется CPU и PPU. Для большинства же игр такой объём явно недостаточен – поэтому большинство картриджей имеют значительно больший объём памяти (до 1024k PRG-ROM и 1024k CHR-ROM). Понятно, что непосредственно процессор не может адресовать такой объём памяти. В Денди применяется метод «страничной адресации» памяти картриджа. Картриджи большого объёма содержат, помимо микросхем памяти, еще и переключатель страниц (маппер). Маппер («mapper») – комбинационная схема (или контроллер), состояние которого однозначно определяет блоки ПЗУ картриджа, отображаемые в данный момент в адресное пространство процессора, или знакогенератор PPU. Размер переключаемых окон адресного пространства CPU зависит от типа маппера, а с некоторыми мапперами может даже варьироваться, типичные конфигурации - 2*16k, 2*8k+16k, 4*8 k (возможны и иные конфигурации - причем, как правило, последнее окно непереключаемое).
Видов мапперов существует очень много (наиболее распространены около десяти) – каждый из них имеет свой функционал по управлению аппаратной частью картриджа: поддержка того или иного вида и объёма используемых в картридже микросхем памяти, возможность использования дополнительной памяти (ОЗУ), наличие других дополнительных функций (звукового процессора, таймеров и пр.). Мапперы имеют свои (отличные от других) «системы команды» управления их состоянием. Большим количеством модификаций «классических» мапперов мы обязаны «пиратам» - стремящимся либо с сэкономить на железе, любо запихнуть на один картридж много всего разного, а зачастую - и то и другое сразу ;-) Несмотря на все разнообразие (см. перечень мапперов), базовый функционал всех без исключения мапперов по большому счету идентичен – состояние регистров маппера определяет (управляет) старшими линиями адреса микросхем PRG-ROM и/или CHR-ROM (для которых не хватает адресных линий в разъеме картриджа).
И так, при наличии маппера, картридж Денди содержит:
PRG-ROM – микросхема ПЗУ, хранящая программу и данные (неотъемлемая часть любого картриджа). Подключается к шинам процессора. Старшие линии адреса микросхемы ПЗУ, а также управляющие входы подключаются к мапперу (если маппер предусматривает переключение страниц PRG-ROM).
CHR-ROM/ CHR -RAM – микросхема памяти, подключаемая к шинам PPU - возможен один из двух вариантов:
- ПЗУ (CHR -ROM) – хранит заранее «прошитые» знакогенераторы PPU, которые могут переключаться, если это предусматривает маппер. Подключается к шинам PPU и мапперу (если маппер предусматривает переключение страниц знакогенератора).
- ОЗУ (CHR -RAM) – статическая память, размером 8k (два знакогенератора). В этом случае «иконки» знакогенераторов должны храниться в PRG-ROM (или генерироваться алгоритмически) и программно (через регистры управления PPU) загружаться в CHR-RAM . Преимуществом такой организации является то, что есть возможность изменения одной иконки знакогенератора (или даже ее части), при сохранении всех остальных иконок неизменными. В предыдущем случае (CHR -ROM ) имелась возможность только выбирать заранее созданные и прошитые в ПЗУ пресеты знакогенераторов (если их несколько) – зато путем переключения можно намного быстрее изменить группу иконок, чем поштучно их подгружать.
Mapper – комбинационная схема (или контроллер), осуществляющие коммутацию блоков микросхем ПЗУ в адресные пространства CPU и/или PPU – т.е. фактически управляет старшими линиями адреса микросхем ПЗУ, для которых «не хватает» адресных линий в слоте картриджа и/или адресном пространстве процессора. Маппер находится на картридже и подключается к шинам CPU и/или PPU и соответствующим микросхемам памяти. Некоторые мапперы состоят из нескольких микросхем. Любая программа (игра) пишется под определенный тип маппера. Т.е., говоря современным языком: драйвер маппера «вшивается» в код программы, более того – он зачастую «размазан» по всему коду … Возможность адаптировать (модифицировать) программу (дамп игры) под другой маппер – задача довольно рутинная – нужно найти и откорректировать все места, где происходит управление маппером.
Также картридж (в редких случаях) может содержать:
SRAM (она же WRAM) - (Адресное пространство CPU $6000-$7FFF) – Статическое ОЗУ, может питаться от компактной батарейки, располагающейся на картридже. Предназначено для «сохранения» игр (если есть батарейка), или просто служит дополнительным ОЗУ (хорошее дополнение). Для него зарезервировано «окно» в адресном пространстве CPU размером 8k (в 4 раза больше встроенного в приставку ОЗУ, а скорость таже и прямая адресация) - существуют картриджи, несущие на борту до 32k ОЗУ (т.е. 4 страницы, переключаемые маппером в этом окне).
Expansion ROM\RAM - (Адресное пространство CPU $5000-$5FFF) – Дополнительные 4k памяти (для CPU). В целом аналогично SRAM (используется, как правило, с маппером MMC5).
VRAM - (Адресное пространство PPU $2800-$2FFF) – «Недостающие» 2k VRAM для двух экранных страниц PPU (3 и 4). Редкая фишка - используется очень немногими играми.
Звуковой процессор - предназначен для более качественного синтеза звука, нежели это реализуется встроенным pAPU (например, в мапперах VRC6, VRC7 и других).
Большинство аппаратных возможностей Денди могут быть использованы (дополнены), например, маппером MMC5 или MMC3 (большинство технологичных игр используют именно их). Разумеется, ничто не мешает разработать новый маппер самому (в архитектуре приставки ничего менять не придется! - всю логику управления маппером содержит программа на картридже), чем китайские разработчики («пираты») увлекаются больше других, а вместе с этим производят и совместимые аналоги «официальных» микросхем мапперов – наиболее популярные из которых следующие:
Программирование мапперов.
В адресном пространстве CPU - адреса $8000-$FFFF выделены для отражения на них памяти картриджа - PRG-ROM. Логически может быть организовано несколько окон, управляемых маппером. Размер и количество окон определяется архитектурой маппера, некоторые типы предусматривают переменный размер переключаемого окна (например, MMC 3). Наиболее распространенный вариант 2*16k, 2*8k+16k или 4*8k - в последнее окно ($xxxx-$FFFF) позиционируется или «хвост» ПЗУ, или его начало. Также, зачастую, последнее окно переключению не подлежит (в нем находятся вектора прерываний).
Каждый маппер имеет определенное количество регистров управления, доступных только для записи. Адресуются они через области, отведенные для PRG-ROM. То есть – чтение из памяти по адресам $8000-$FFFF приводит к чтению ПЗУ (PRG-ROM), а запись, по некоторым адресам из этого диапазона, приводит к записи управляющих слов в соответствующие регистры маппера.
Подробное описание разных мапперов – не цель данного материала, однако некоторые из них рассмотрим подробнее.
Маппер UNROM.
Для примера рассмотрим маппер UNROM – один из самых простейших мапперов, но весьма удобный для знакомства с программированием Денди и написания несложных программ. Он предусматривает работу с PRG-ROM объёмом 128k (17 линий адреса, 3-мя старшими линиями управляет маппер), в качестве CHR используется непосредственно ОЗУ размером 8k (без участия маппера). Маппер строится на базе двух микросхем (74HC161 и 74 HC 32) и программно представлен как один регистр, который отражается на всё пространство PRG - ROM ($8000-$ F FFF). То есть запись по любому адресу из этого диапазона приведет к записи в регистр маппера. Записываемый байт (а вернее 3 младших его бита) определяет блок микросхемы ПЗУ (16k), подключаемый в окно $8000-$BFFF (т.е. при A 14=0) - блоки нумеруются с нуля и с начала адресации микросхемы ПЗУ. В окно $C000-$FFFF (т.е. при A 14=1, вне зависимости от содержимого регистра маппера) всегда подключен последний блок (последние 16k) ПЗУ ( A 14= A 15= A 16=1 на адресных входах ПЗУ). Таким образом, возможен вариант (при « xxxxx 111» в регистре маппера) когда последний блок ПЗУ будет последовательно продублирован в адресном пространстве.
Знакогенератор CHR - RAM - заполняется программно, через регистры PPU ($2006,$2007) – запись должна производиться в адресное пространство PPU, отведенное под знакогенератор ($0000-$1FFF). Вид отражения экранных страниц определяется распайкой (коммутацией) перемычки на плате картриджа.
Принципиальная схема маппера UNROM такова:
Разумеется, на схеме выбор (из двух) логических входов элементов 2-ИЛИ может быть произвольным, как и выбор используемых 3 из 4 таких элементов 2-ИЛИ из микросхемы 74HC32 (аналог «ЛЛ1») – на рисунке изображен один из множества возможных вариантов (точная копия коммутации на примере реализации в картридже на плате HVC-UNROM-03). Счетчик 74HC161 (аналог «ИЕ10») в данной схеме используется фактически как регистр (а не счетчик).
Super Hik.
Маппер 49 (Super Hik 4-in-1).
Многие игры используют далеко не весь функционал, предоставляемый маппером MMC3, и в первую очередь в части размера PRG и CHR. Таким образом, выглядит вполне логичным (с технической точки зрения) создание мульти-игрового картриджа без микросхемы ОЗУ ( WRAM ), где под каждую из игр зарезервирован слот размером 128k (и в PRG и в CHR) – т.е. применение микросхем емкостью 4 мегабита (19 адресных линий). Игр, которым такой объем ресурсов совместно с маппером MMC3 (его аналогом AX5202) достаточен - предостаточно!
Рассмотрим механизм переключения слотов с играми такого мульти-игрового картриджа с точки зрения схемотехники и управления ей. Фактически мульти-игровой картридж « Super Hik» содержит в себе два маппера! ММС3 (полноценный, аппаратный) – именно с ним работают игры (выбранная в данный момент игра), и «маппер верхнего уровня» (этакий «гипервизор») – который обеспечивает выбор слота с игрой, которая и будет запущена … А так как для каждой из игр маппер верхнего уровня прозрачен (что логично), рассмотрим именное его принцип работы.
Маппер верхнего уровня представляет собой регистр «только для записи», доступный в диапазоне $6000-$7FFF. Т.к. за доступ к данному диапазону адресов отвечает MMC3, то необходимо сначала разрешить такой доступ (управляющая команда к MMC3). Конечно жаль, что из-за такого способа обращения к регистру маппера теряется возможность установки на картридж WRAM и стало быть запуска игр, требующих дополнительное ОЗУ по этим адресам, но сейчас не об этом.
Общая цель маппера верхнего уровня проста – обеспечить выбор (адресацию) старших адресных линий микросхем ПЗУ (PRG и CHR) за пределами 128 k , которыми управляет MMC 3. Т.е. должна быть возможность управления линиями A17 и A18 у обоих микросхем ПЗУ, хотя и изменять состояние этих линий предстоит вобщем то не часто: при включении/сбросе приставе на этапе выбора игры (слота с игрой внутри микросхем памяти).
Принципиальная схема «маппера верхнего уровня» внутри «маппера 49» такова:
Таким образом, в регистре маппера присутствуют только 5 бит. С программной точки зрения возможны два режима работы маппера (выбираемые битом D0) в части переключения страниц PRG-ROM.
«Основной режим» ( D 0=1) – в этом режиме слот с игрой (один из четырех) выбирается битами D6 и D7 (выполняют роль сигналов адресных линий A17 и A18 физических ПЗУ как PRG-ROM, так и CHR- ROM ); значение бит D4 и D5 в регистре - безразлично; адресными линиями A13-A16 PRG- ROM и A10-A16 CHR- ROM управляет маппер MMC 3. Игра не выходит за пределы своего слота и управляет только маппером MMC3.
«Альтернативный режим» ( D 0=0) – именно этот режим активируется при загрузке (сбросе), т.к. аналоговая часть схемотехники маппера верхнего уровня (RC-цепочка) сбрасывает весь регистр маппера в ноль. В этом режиме MMC3 не управляет адресными линиями PRG-ROM – ими управляет исключительно маппер верхнего уровня. Биты D6 и D7, как и прежде, подключены к линиям A17 и A18 как и PRG-ROM, так и CHR- ROM (т.е. для CHR-ROM оба режима абсолютно одинаковы) – а вот биты регистра маппера D4 и D5 теперь задают значения адресных линий A15 и A16 PRG-ROM соответственно, а линии A13 и A14 берутся непосредственно со слота картриджа. В этом режиме получается программная модель переключения страниц PRG-ROM, в некоторой степени, аналогичная мапперу UNROM – в данном случае 4 старших бита регистра маппера выбирают одну из 16 страниц размером 32k микросхемы PRG-ROM, подключаемую в окно $8000-$ FFFF (после сброса – самое начало ПЗУ). Повторюсь: переключение страниц CHR - ROM в «альтернативном режиме» происходит аналогично «основному режиму» - линии A 17 и A 18 выбираются маппером верхнего уровня (биты D6 и D7), более младшими битами CHR-ROM всегда управляет маппер MMC3.
В целом, для использования такого симбиоза мапперов в конечном решении, необходимо как минимум вносить изменение в дамп игры, помещаемый в первый слот. Т.е. искать игру у которой, хотя бы, хвост первых 32k дапма свободен для размещения векторов прерываний, а также в этом блоке есть место для размещения их обработчиков (в лучшем случае - кода для реализации меню выбора игр, или накрайняк – для переключения банков «альтернативным способом» на более свободный, где уже и разместить код меню). Не забываем, что картинку меню придется складывать из иконок выбранного блока CHR-ROM (что возможно повлечет необходимость или выбора одной из игр с подходящими символами в одной из страниц, которую нужно будет выбрать двумя мапперами, или поиск игры с неиспользуемыми ячейками CHR-ROM, в которые можно разместить нужные символы).
Общие сведения об архитектуре Денди.
Игровая приставка Денди является микро-ЭВМ для домашнего (игрового) применения (далее речь пойдет именно о Денди – как наиболее распространенной вариации консоли NES, а вернее ее японской версии Famicom, в России), ориентированная на использование телевизора в качестве дисплея. Подключение к телевизору может осуществляться одним из двух способов – «по низкой частоте» (двумя кабелями – звук и композитный видео) или «по высокой частоте» (при помощи «антенного» кабеля). Китайские совместимые поделки, представленные в нашей стране в большом разнообразии – архитектурно совместимы с Денди, однако зачастую имеют ряд схемотехнических «упрощений», не позволяющих полностью реализовать функционал оригинальных консолей (и даже стиплеровской «Денди»).
Денди и прочие клоны консолей NES / Famicom (как в общем-то и их оригиналы) сконструированы на базе микропроцессора (CPU), совместимого с MSC6502 (микросхемы: RP2A03, RP2A07, UM6527, HA 6827 и пр.) и видеопроцессора (PPU) (микросхемы: RP2C02, RP2C07, UM6538, UM6528, HA 6838 и пр.), которые работают в тесном взаимодействии (такая вот «двухпроцессорная» система). Пары CPU + PPU подбираются с учетом формата формируемого кадра – NTSC или PAL . У каждого процессора своё адресное пространство и своя оперативная память (не путать с адресным пространством). Адресные пространства CPU и PPU не пересекаются . Микросхема CPU, применяемая в Денди, имеет встроенный звуковой сопроцессор – pAPU (чего нет у базового процессора 6502), но при этом отсутствует блок двоично-десятичной арифметики. Помимо CPU и PPU, совместно со статической оперативной памятью, работу приставки обеспечивают так же прочие «мелкие» компоненты (регистры, логические элементы), сопрягающие работу всей системы. Архитектура Денди предусматривает хранение (и исполнение) программ со сменных модулей – «картриджей». Об архитектуре картриджа см. ниже – здесь лишь скажем, что часть картриджа адресует CPU, а часть PPU. Изначально плата приставки Денди содержала несколько корпусов микросхем (CPU и память, PPU и память, регистры логические элементы и прочее …) – как и оригинальные консоли. Последние ревизии моделей Денди (1996 год) собраны на заказной микросхеме (UM6561A) – которая совмещает на одном кристалле все «микросхемы» приставки (SoC – System on Chip). В ревизиях моделей Денди более ранних версий можно встретить «гибридный вариант» - память (оперативную и видео) установленную в виде двух отдельных микросхем (UM6516, UM6116 – аналог КР537РУ10), видимо, в центральный «SoC» (xx1818) её нет.
Вне зависимости от элементной базы, структурная схема Денди имеет вид, показанный на рисунке 1.
Как писались игры на Dendy
Этой статьей я хотел бы немного пролить свет на то, как создавались игры на Dendy. Причем речь пойдет не о том, как это можно делать сейчас, а как это происходило тогда — в 80-е и 90-е годы, и о том, с какими проблемами сталкивались разработчики в то время. Если вам наскучило читать очередные воспоминания менеджеров, дизайнеров, или программистов, переквалифицировавшихся в менеджеров, которые раскрывают техническую сторону дела чуть менее чем никак, то добро пожаловать под кат.
Тогда и сейчас
В наше время разработка для игровых приставок все больше напоминает написание программ для обычных компьютеров, разница между тем что творилось тогда и тем, что имеет сегодняшний разработчик колоссальна. С одной стороны, сегодня, технологии и культура разработки шагнули далеко вперед, позволив вести разработку на языках высокого уровня, с другой — в 80-е фирмы, выпускающие приставки, еще не до конца понимали что следует дать сторонним разработчикам для создания игр. И поэтому если сейчас для очередной playstation можно получить документацию, пяток демо-дисков, а также мощную девелоперскую станцию, по сути гибрид самой приставки и компьютера общего назначения, пригодного для непосредственной разработки, то например в 80-е многие разработчики довольствовались книжечкой со спецификацией консоли. А вот за аппаратной частью приходилось идти на радиорынок (или что там было в штатах вместо них?).
Немного истории
Принято считать, что изначально была создана Nintendo Entertainment System (NES), с которой уже была спирачена наша Dendy и десятки других клонов по всему миру.
После взгляда на картинку видно, что это, возможно, не совсем так. Изначально, в Японии в 1983 году, все-таки была выпущена консоль Famicom. В любом случае существовало 2 незначительно отличавшихся консоли — NES и Famicom. И разработка для них тоже велась разными инструментами, хотя программный код игр в картриджах для обеих был одинаков. Несколько различались сами картриджи. В случае NES на картридже присутствовало несколько дополнительных контактов, которые вели к чипу, который в теории должен был быть только на лицензионных картриджах…
В далеком 1983 году Nintendo могла только мечтать о сторонних разработчиках для новоиспеченной консоли. Так что первые несколько игр были созданы самими авторами консоли. Некоторые из них были портами игр, уже существовавших на аркадных автоматах. Эти игры отличались примитивностью даже по меркам NES, не используя всех возможностей ее аппаратной начинки. Все игры того стартового периода умещались на один экран и не занимали более 32 килобайт данных на картридже. Наиболее известные игры того периода: Donkey Kong, Balloon Fighter, Mario Bros. Ирония заключалась в том, что NES имела специальную аппаратную поддержку для скроллинга уровней, но самые первые игры сайд-скроллерами не являлись, хотя и были выпущены компанией, которой сам бог велел, зная все особенности их консоли, выпускать игры, максимально подчеркивающие преимущества новой приставки.
Ограничение для размера NES-игр в 32 кб имело место только первые пару лет. Потом сторонние разработчики более-менее приноровились к консоли и поняли, что 32 кб хватит далеко не всем. Видя эту ситуацию, Nintendo решила сделать для всех новых картриджей маппер, позволявший создавать игры на порядок большего размера. Тут надо заметить, что сама архитектура NES не менялась, но менялись картриджи, расширяя возможности самой NES.
На картридж можно было засунуть все что угодно — любые чипы, которые душа пожелает: оперативную память, видеопамять, сопроцессор, энергонезависимую память для сохранений. Теоретически в картридж можно добавить такие вещи как модем или raspberry pi. Другое дело что для реализации этого потребуются титанические усилия, а также несколько свободных выходных.
Картриджи
Спецификации NES-картриджа до сих пор могут быть найдены в интернете, и, вообще, довольно подробно разобраны сообществом. Но сам процесс производства картриджей — это тайна покрытая мраком. Более менее достоверно известно что на территории Европы и США выпуском картриджей занималась исключительно Nintendo. В смысле, самих коробочек с микросхемами.
Набор микросхем увеличивался с годами, так что можно сказать, что с точки зрения разработчика спецификация самой консоли менялась, хотя менялись на самом деле картриджи. Эта особенность позволила NES быть популярной столь долгое время, конкурируя с другими игровыми приставками.
Каждый разработчик должен был явно указывать в заголовке скомпилированной игры(ROMа) перечень функций NES, которые будут использованы игрой. Некоторые функции поддерживались «обычными» картриджами — например маппер памяти, а вот за наличие сохранения или лишней оперативной памяти приходилось доплачивать Nintendo, чтобы та включила заветные чипы в ваш будущий картридж.
В Японии были компании, которые сами занимались изготовлением картриджей, попутно добавляя в них собственные уникальные чипы, создававшиеся под одну конкретную игру. Впоследствии подобные вещи в себе съели немало нервных клеток у тех, кто создавал эмуляторы NES, пытаясь заставить работать в своем детище и такие игры.
Архитектура картриджей подразумевала что сама программа и графические спрайты находятся в разных чипах. Вот например фотография картриджа Super Mario Bros.
В тех картриджах, что продавались у нас, подобные чипы использовались обычно только в тех, что шли вместе с консолью, и то не всегда. В пиратских картриджах использовались черные кляксы вместо чипов, скорее всего это были те же схемы, но изготовленные по другой технологии. По какой? Думаю в комментариях вы найдете ответ на этот вопрос.
Оборудование для разработки
Сведения об официальном SDK от Nintendo весьма скудны, настолько, что я уже склонен считать, что его просто не было. Т.е. в сети есть пара фотографий подобных юнитов, но нигде не сказано что они были выпущены Nintendo, более того — их вид говорит о том, что это были скорее всего поделки конечных разработчиков. Так что все, что было доступно — спецификации, а дальше каждый разработчик крутился как мог. Самодельное же оборудование для разработки делилось на 2 класса: видоизмененные картриджи с перезаписываемыми банками данных и отладочные станции.
В первом случае все относительно просто — в обычном картридже банки данных и графики заменялись на схожие чипы с возможностью многоразовой перезаписи. Нельзя сказать, что разработка с таким инструментом радовала — после каждой перекомпиляции картридж приходилось прошивать заново. И да, несмотря на малый объем программ, скорость прошивки была невелика, учитывая оборудование и мощность компьютеров того времени. Тем не менее этот девайс в нескольких экземплярах был неизменным и часто единственным орудием программиста.
В случае RAM-картриджей разработка шла гораздо быстрее. Можно было редактировать память прямо в процессе игры, используя для этого компьютер, на котором, собственно, и велась разработка. Чип EEPROM заменялся на банки оперативной памяти, которые с одной стороны виделись консолью как обычный картридж, а с другой стороны подключались к компьютеру разработчика и были для него обычной оперативной памятью… или диском… или устройством — все зависело от того, какой именно драйвер программист писал для своего детища.
Наиболее упорные или удачливые разработчики могли похвастаться отладочными станциями — модифицированными NES, которые вдобавок ко всем плюсам RAM-картриджей давали еще и возможность проводить глубокую отладку, просматривая содержимое видеопамяти, регистров процессора и т.п. Вот например одна такая станция.
На каких компьютерах проходила разработка — неизвестно, но, учитывая тот уровень возни с радиодеталями, который требовался чтобы запустить отладочную версию игры на приставке, особого значения модель компьютера не имела. Точно известно, что японские разработчики применяли компьютеры MSX. С большой долей вероятности можно сказать, что в США применялись компьютеры Apple 2, ввиду того, что они были довольно распространены, а также имели процессор, аналогичный используемому в NES.
Программное обеспечение
Основной и практически единственный язык программирования использовавшийся для разработки игр — ассемблер, некоторые счастливчики писали на С, если могли достать компилятор. Но даже в этом случае, некоторые фирмы не покупали у Nintendo ассемблер а писали свой собственный. Чем это было вызвано сказать тяжело, но многие источники говорят о том, что в первые годы существования NES Nintendo не делилась своими инструментами со сторонними разработчиками.
Процессор, который использовался в NES, также имелся и в компьютерах вроде Apple 2 или Commodore 64, для которых были и свои ассемблеры и даже компиляторы С. Но архитектура NES все же имела некоторые отличия, а дикие системные ограничения и наличие банкинга(необходимости выгружать старые и загружать новые куски программного кода в область доступную процессору) не давали возможности писать на С игры, которые бы использовали максимум возможностей консоли.
Ассемблер — далеко не единственная вещь, которую приходилось писать вручную: редакторы графики, программаторы, отладчики: все это было написано по несколько раз заботливыми руками программистов. Отдельной статьей можно считать редакторы карт — следует помнить, что компьютеры были слабы и малопамятны, так что, например, вся карта для Metroid была нарисована вручную на бумаге, а потом уже кусками кодировалась в игру.
Процесс разработки
В разработчики часто набирали вчерашних студентов, не видевших NES в глаза. Для львиной доли мелких компаний создававших игры как нельзя лучше подходил термин «шарашкина контора». Типовая игра занимала 3-6 месяцев времени на разработку. Команды чаще всего были небольшими — 3-10 человек. Было много контор, которые выпускали 1 игру и потом исчезали без следа.
Процесс разработки обычно начинался с создания инструментов для разработки. Хотя концепт игры к тому времени был уже готов. И вообще, в первые годы многие игры для NES были портами с аркадных автоматов. Даже если к программированию самой игры еще не приступали, за дело брались композитор и художник. Практически вся музыка и вся графика для дендивских игр были сделаны не на компьютере.
Сперва дизайнер рисовал эскизы бэкграундов и персонажей, затем, после отбора подходящих рисунков, начиналась так называемая пикселизация — палитра NES поддерживала только 14 цветов на экране одновременно, так что приходилось перераскрашивать рисунки с учетом этого ограничения. Затем рисунок расчерчивался по сетке, и при необходимости растягивался или сжимался — NES аппаратно поддерживала спрайты размером 8x8 пикселей. Персонаж Марио рисовался в 4 прохода, после съедания гриба — в 8 проходов. Мелочь, но иногда из-за ее игнорирования мы видели в некоторых играх пропадающие спрайты, когда на экране становилось очень много врагов, так что программистам приходилось экономить и на спичках. Кстати видеопамяти было 2 вида — одна, в которой обычно хранились спрайты персонажей и прочая мелочевка, вторая, побольше в которой хранились карты уровней. Последняя находилась в отдельном адресном пространстве и поддерживала аппаратно ускоренные операции по скроллингу.
Работа композитора плавно перетекала в работу программиста — написать музыку это только часть дела, закодировать ее в игру — тоже дело наживное, а вот затем начиналось самое интересное. Как было сказано выше, иногда игра подтормаживала, так что если в предрелизе такие тормоза достигали небывалых показателей, то композитору давали задачу «оптимизировать» музыку — убрать лишние опкоды, чтобы ускорить работу игры в целом. Тут-то и выяснялось истинное мастерство композитора. Лишнюю монетку в копилку позора композитора добавлял тот факт, что далеко не всегда разработчик игр по фильму получал лицензию на… использование музыки из фильма. Таким образом композиторам приходилось писать что-то оригинальное, и далеко не всегда хорошее.
Среди игр для NES был распространен так называемый репэкедж — это когда на базе одной игры делают другую меняя спрайты и уровни, иногда немного меняя сам игровой процесс. Например Castlevania и Ninja Gaiden сделаны на одном движке, также как и Darkwing Duck был сделан на базе движка для серии игр Megaman(Rockman). Каковы были истинные масштабы репэкджа мы можем только догадываться, особенно учитывая тот факт, что огромный процент игр на денди — однотипные сайд-скроллеры.
Разработчики
Меня конечно интересовало что же именно разработчики творили в те дни, но увы, именно этот вопрос так и остался без ответа. Программисты ни черта не помнят о том периоде. Т.е. они помнят какие-то организационные моменты, сложности возникавшие в команде, как они ели пиццу по выходным, как спешно искали работу программистом для NES, чтобы расплатиться с долгами, но не могут назвать марку компьютера за которым проводили бессонные ночи. И в целом можно сказать что их работа воспринималась как рутина, у них не было чувства, что они создают великие вещи, которые останутся в памяти многомиллионного поколения на всю жизнь.
Только потом, годы спустя они стали понимать значимость того, что они писали. И тогда им стало по-настоящему стыдно. Их можно было понять — зоопарк консолей того времени, невнятность их будущего, микроменеджмент в разработке — все это создавало впечатление того, что ты пишешь нечто непонятное, непонятно для чего, и непонятно с какими перспективами.
Среди компаний, как уже было выше сказано, было много однодневок — вроде тех, что сейчас пишут игры для айфонов, которые пробовали себя на этом рынке, разорялись, уходили в забвение. Огромное количество дендивских ромов вообще не содержит никакой информации о разработчиках, что говорит о весьма низкой оценке результатов своей работы со стороны программистов.
Удивительно, но остались и такие, что до сих пор продолжают разрабатывать игры, даже спустя 30 лет. Их можно найти в соцсетях, но после того как второй десяток попыток достучаться хоть до одного из них провалился, я бросил это бесполезное занятие. Впрочем список имен разработчиков, которые мне удалось выдрать из наиболее обширной коллекции ромов находится здесь.
Сегодня
Так что сегодня создать игру для Денди может любой студент, написавший, а не купивший код к своей лабораторной по предмету, посвященному программированию микроконтроллеров.
Самодельные картриджи для Dendy/Famicom
После моей статьи про дампер картриджей (которую пока что оставили на Хабре почему-то), меня очень много раз просили рассказать, как собирать и записывать картриджи для Денди/Famicom самому. Да, это очень избитая тема, даже в древних номерах журнала «Радио» про это можно было почитать, но прогресс не стоит на месте. Рассмотрим эту тему с точки зрения современных компонентов. Тем более, по-моему, она идеально подходит для изучения азов работы с ПЛИС, именно на этом я и сам учился.
Как и в прошлый раз, эта публикация в двух форматах: развлекательное видео попроще (да, уже третья серия) и обычная статья с более детальной информацией. Кому как больше нравится, но лучше посмотрите и то, и другое.
Видео:
Статья:
Начать, наверное, нужно с того что, картриджи, которые продавались и продолжают продаваться в наших магазинах, перезаписать, увы, не получится (на самом деле некоторые можно, но об этом в другой раз). Связано это с тем, что в них установлена обычная EPROM память, которую можно записать только один раз. Однако, ничто не мешает собрать свой собственный картридж с нуля.
Напомню, что картридж включается прямо в шину CPU и в шину PPU, а соответственно в первую очередь содержит две микросхемы памяти с параллельным доступом: PRG — к ней обращается процессор, и она содержит непосредственно код игры, и CHR — с ней работает PPU (графический процессор), и она содержит изображения. При чём последняя запросто может быть не ПЗУ, а оперативной памятью, куда уже в процессе игры записываются данные.
Таким образом, самый простой картридж можно сделать из любых двух микросхем памяти с параллельным доступом, будь то хоть EPROM, хоть flash. При этом больше ничего из обвязки не нужно. Например, вот так выглядел мой первый самодельный картридж:
Микросхемы просто подключаются напрямую к соответствующим выводам на разъёме картриджа. Ноги /RD можно припаять напрямую к земле, ведь консоль всегда будет только читать данные, но я записывал данные уже после сборки картриджа, с помощью всё того же дампера, поэтому подключил все выводы как положено. Биты адреса и данных при этом перепутаны местами, но это абсолютно не имеет значения. Внизу можно увидеть перемычку, которая определяет «mirroring» — как будет зеркалироваться видеопамять: горизонтально или вертикально. Это зависит от игры, и в простейших играх определялось именно перемычкой на картридже.
И ещё очень важный момент — активировать нашу память нужно только тогда, когда консоль обращается к картриджу, иначе будет возникать конфликт на шине. Для этого у микросхем есть вывод /CE (chip enable), который включает память. Тут в ход идёт достаточно простая математика. Программная (PRG) память картриджа начинается с адреса $8000 и заканчивается $FFFF, это два в пятнадцатой степени. Графическая (CHR) память картриджа начинается с адреса $0000 и заканчивается $1FFF, имея объём в 8 килобайт, а это два в тринадцатой степени. Соответственно включать нашу память надо пятнадцатым и тринадцатым контактом на адресной шине. На разъёме картриджа уже есть специальные выводы, которые выдают необходимый нам сигнал. Более того, в случае с PRG памятью нужный нам контакт так и называется — /ROMSEL — сокращённо от ROM Select. Туда консоль выдаёт 0 вольт, когда обращается к памяти картриджа в районе между $8000 и $FFFF. Всё проще некуда.
Однако, на такой картридж можно будет записать только самые простейшие игры. Более серьёзные уже используют картриджи с мапперами, чтобы увеличить максимальный объём игры. Попробую объяснить, как они работали.
У памяти с параллельным доступом каждый бит адреса задаётся отдельным выводом у микросхемы. В разъёме для картриджа есть выводы A0-A14 (15 выводов) для PRG памяти. Это соответственно 15 бит адреса, которые дают 32768 комбинаций единиц и нолей, т.е. позволяют адресовать 32 килобайта. Для CHR памяти там соответственно выводы A0-A13, это 16384 комбинаций, т.е. 16 килобайт, но половина из них отдана памяти внутри консоли.
Уже в восьмидесятые годы таких объёмов стало не хватать. Конечно ничто не мешает поставить в картридж память бОльшего объёма, но у такой памяти и адресных выводов больше. Не трудно посчитать, что каждый дополнительный вывод увеличивает количество возможных адресов ровно в два раза. Но куда их подключать, если количество контактов в разъёме картриджа ограничено? Вот тут на помощь и приходят мапперы, именно они управляют дополнительными выводами в зависимости от различных условий. Почти всегда такими условиями является попытка запись в PRG-область памяти картриджа. Да, в ту, куда нельзя ничего записать.
Многие игры используют для этих целей простейшие логические микросхемы. Например, в картридже у Battletoads стоит четырёхбитный счётчик 74161, который используется как триггер. При записи по любому адресу от $8000 до $FFFF он запоминает записанное значение и выдаёт его на те самые дополнительные выводы у памяти, он же переключает мирроринг.
Но большинство игр использует для этого более сложные микросхемы, которые разработаны специально для этих целей.
Они, как правило, умеют уже переключать разные банки для разных областей памяти, управлять дополнительной памятью, генерировать прерывания, а иногда даже расширять вычислительные мощности консоли.
Первым делом читаем, как происходит взаимодействием с ним. А происходит оно через запись по определённым адресам, их 8 групп: $8000-$9FFE (чётные), $8001-$9FFF (нечётные), $A000-$BFFE (чётные), $A001-$BFFF (нечётные), $C000-$DFFE (чётные), $C001-$DFFF (нечётные), $E000-$FFFE (чётные) и $E001-$FFFF (нечётные). Запись по любому адресу внутри группы равнозначна. Видите закономерность? Регистр выбирается с помощью трёх адресных бит: A0, A13 и A14, остальные же значения не имеют.
Попробуем же имитировать работу маппера с помощью ПЛИС. Код я пишу на языке Verilog. Он тут не подсвечивается, прошу прощения за это.
Сначала описываем наши регистры, которые хранят текущее состояние:
Описываем реакцию на запись по соответствующим адресам. Возрастающий сигнал /ROMSEL говорим о том что было обращение к памяти картриджа, т.е. по адресам $8000-$FFFF, нам надо реагировать именно в этот момент.
Теперь же опишем, какой должен выбираться банк при обращении к соответствующей части памяти в зависимости от наших регистров.
Переключаются они в соответствии с такой таблицей:
Теперь CHR. Там такая схема:
Режим зеркалирования описывается всего одной строкой. В зависимости от него мы замыкаем вывод картриджа CIRAM A10 либо на A10, либо на A11:
Дальше сложнее. MMC3 умеет генерировать прерывания, когда на экране рисуется определённая строка. Это весьма полезно, и игры часто это используют. Строки на экране считаются с помощью обращений к A12 у PPU. При типичных настройках сигнал на A12 переходит из логического 0 в логическую 1 ровно один раз за строку, если не считать кратковременные переходы в 0. А их надо не считать, это всё немного усложняет:
Ах да, MMC3 поддерживает ещё подключение дополнительной оперативной памяти по адресу $6000-$7FFF! Надо не забыть и это описать:
На самом деле собрать картридж для какой-то одной определённой игры весьма просто, ведь нужно будет установить только необходимые компоненты. А вот сделать универсальный картридж гораздо сложнее.Если установить ПЛИС на 128 макроячеек, flash на 512 килобайт для PRG, flash на 512 килобайт для CHR, SRAM на 32 килобайта для CHR, SRAM на 32 килобайта в качестве дополнительной памяти, питание которой поддерживается батарейкой для игр, которые умеют сохраняться, то на нём пойдёт уже около 90%-95% игр. Схема получается весьма замороченная, я долго вручную рисовал плату под всё это дело. Кстати, при выборе компонентов не стоит забывать, что у Famicom/Dendy пятивольтовые уровни. Китайцы сейчас очень часто это игнорируют.
Первая ревизия моего универсального картриджа выглядела как-то так:
Ну и программу для записи игр написал конечно же:
Как видите, всё не так сложно, если немного посидеть и разобраться в принципах работы.
Архитектура и программирование Dendy.
Игровая приставка NES (Nintendo Entertainment System) - версия для Америки, известная также как Famicom (FAMily COMputer) - версия для Японии, в нашей стране, благодаря фирме Стиплер (Steepler), известна как Dendy. И не смотря на то, что в середине девяностых годов прошлого века Денди была действительно «народной» приставкой – со стремительным ростом технического прогресса она сейчас, как игровая система, отошла далеко-далеко на задний план (уступив место ПК и другим игровым консолям). Но именно в сегодняшние дни Денди более интересна не геймерам, а радиолюбителям – как простейшая микро-ЭВМ …
Вопросом программирования Денди заинтересовался и я.
В начале изучения и создания материала, который представлен вам ниже, а именно в 2003 году - разнообразной информацией Интернет не изобиловал (хотя кое-что и было) – вся документация англоязычная (хотя встречались и ее прямые переводы – безо всякого осмысления переводимого). Тогда же вышла даже книга и мультимедийный диск (разных издательств, но одинакового содержания – книга даже более полного) – в которых легко узнавались все теже англоязычные источники (а часть информации взята «с потолка» и абсолютно некорректна). Поэтому многое пришлось осмыслять на практике (весь текст исключительно мой, авторский).
Вторая итерация и второе существенное дополнение и переработка данного материала была в 2007 году … Всего и не вспомнить, некоторые несущественные дополнения и корректировки были и до и после. Также благодаря отзывам читателей были исправлены некоторые недочеты, однако не все изменения сразу публиковались на сайте.
И вот в 2016 году текст очередной, что называется «обновленной и дополненной», редакции перед вами …
Материал ориентирован на читателя, обладающего базовыми сведениями по организации ЭВМ.
Читайте также: