image

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

Если вы не хотите его проходить, то:

  • возьмите готовый эмулятор;
  • повторите то, что в нём уже сделано.

Но если это не ваш путь, то милости просим в статью.

Вы всё же решили заглянуть в статью? И в самом деле захотели сами с нуля написать эмулятор? Или хотя бы желаете немного узнать о том, как это делать?

Хорошо, тогда я постараюсь вас отговорить от этого «гиблого» дела!

Меня зовут Сергей. Я пишу эмулятор Nes на Pascal, и уже написал статью, где рассматриваю разработку процессора. Также сделал перевод статьи, где люди делали многое до меня. На момент написания этой статьи уже выпущена версия PZ_Nes, и небольшой опыт в написании эмуляторов я уже набрал (может, это кому-то покажется не таким уж и малым опытом, а кому-то очень малым, но я сравниваю себя с собой).

С учётом всего того, что я написал выше, статья не будет касаться именно Nes.

Статья будет относиться к эмуляции какого-либо устройства.

▍ С чего начинать?


Всегда надо начинать с банального. И самое банальное — это выбор эмулируемого устройства. И выбор ваш должен падать на наиболее простые приставки или ПК (возможно, игровой автомат?). Лично я новичкам, наверное, не советовал бы даже Nes эмулировать. Это не так просто, как кажется, потому начните лучше с Chip-8 (здесь гляньте и здесь, а также поищите по Хабру и вообще по интернету, немало информации по нему). А также есть ещё более простые системы, где эмулируется несколько инструкций, несколько регистров и взаимодействие с пользователем (LC-3).

Дальше вы должны ознакомиться с устройством. Для этого необходимо:

  • иметь само устройство или его эмулятор (лучше первое).
  • уметь программировать на ассемблере для данного устройства (не учитывая ЯП, на котором вы сами программируете).
  • желательно знать схемотехнику (даже больше, лучше иметь опыт в схемотехнике).
  • получить как можно больше информации об устройстве, которое собираетесь эмулировать (помимо того, что оно у вас есть, есть ещё документация, схемы устройства, информация, как работают сигналы, как работает процессор и всё, что относится к эмулируемому устройству).
  • посмотреть другие эмуляторы по данному устройству (если они есть) и их реализацию (если вы не делали до этого эмуляторы).

Постарайтесь не смотреть симуляторы. Они вложат в вас лишнюю информацию поначалу (но если вы хорошо разбираетесь в теме, то, возможно, стоит взглянуть).

На это у вас должно уйти минимум неделя. Найти, структурировать информацию, сохранить себе, изучить найденную информацию.

Напоминаю — я ориентируюсь на то, что вы решили реализовать эмулятор своими силами!!!

Если вы думаете, что я шучу, то зря. Если какую-то информацию вы не нашли сейчас и не выделили для себя, то в процессе эмуляции вы можете столкнуться с ситуацией, когда нужно будет что-то реализовать, но вы не знаете как. И вам заново придётся перерывать множество информации. Я по сей день возвращаюсь к процессору 6502, потому что исправляю ошибки, которые я допустил, и очень часто вношу новые согласования с подключаемыми к процессору устройствами.

▍ Так когда же начинать делать эмулятор?


Делайте его хоть с первого дня вашей затеи. Ни хуже, ни лучше не будет. Вы в любом случае набьёте шишки и будете рвать волосы на голове.

Важно: делайте паузы в работе и периодически отвлекайтесь.

Не думайте, что чем раньше вы начнёте делать, тем раньше закончите. Дело в том, что если начинаешь что-то делать, то в большинстве своём это невозможно «закончить». Можно подвести к концу разработку. А улучшать эмулятор (вашу программу) можно до бесконечности.

Но начать всегда важно! Если не начнёте, то ничего и не сделаете.

▍ Не бегите впереди паровоза


Всегда начинайте с эмуляции процессора. Это будет одно из самых долгих занятий. Как вы читали выше, вам надо быть уже знакомым очень хорошо с процессором и понимать, как работают инструкции, сколько регистров, какие регистры, какие флаги существуют для данного процессора, как они работают в инструкциях (взаимодействуют с инструкциями). С какой памятью работает процессор, как он с ней работает. Работает ли с памятью (работает! Если вы возьмёте какие-то новые процессоры и будете утверждать, что процессор не работает с памятью, то вы не правы, у процессора есть кэш-память, с ней он работает напрямую, у всех процессоров есть регистры — а это тоже память!).

Не надо начинать с эмулирования других устройств эмулируемого устройства (не, ну мыло мыльное… имеются в виду подключаемые внешние и внутренние устройства «приставки», которую вы хотите эмулировать), если у вас нет процессора. Вам как минимум будет сложно проверить работоспособность этих устройств. Если вы можете запустить процессор и вхолостую его погонять, то без нагрузки подключаемые устройства вы не сможете проверить.

Процессор сразу же работает с памятью, и если в памяти записаны нули, то процессор уже может читать эти нули и обрабатывать их.

Не пытайтесь сделать всё сразу. Лучше всего запишите, что вы хотите сделать, и пошагово реализуйте процесс (да, в большинстве случаев я так не делал, за что зачастую плачу вновь, перечитывая документацию и прочую информацию).

Ну вот вы реализовали процессор, что же дальше? Вы, конечно же, возьмётесь за эмуляцию видеоадаптера устройства. Но не надо этого делать, если вы в самом деле решили создать эмулятор, то дальше надо потратить очень много времени опять не на эмулятор…

У вас есть процессор, вам надо его протестировать, но вы не можете узнать, работает он или нет (ладно-ладно, ранее я уже проговорился, что процессор мы можем вхолостую проверить). Если вы не хотите очень много проблем, то вам нужно протестировать работу всех инструкций процессора (постараться хотя бы протестировать основные инструкции), работу флагов, регистров (если это нужно) и взаимодействия с памятью.

▍ Так что же делать дальше?


А для этого надо делать отладчик. Нет, ну вы, конечно, можете взять готовые тесты для процессора. Погонять его, посмотреть в обычном отладчике, что там происходит, но представьте, что у вас 65 тыс инструкций в процессоре. И все надо проверить… Я думаю, вручную вы точно не захотите это делать. А значит, запускаем процессор, заставляем его выдавать логи о его работе и на этом можно завершить данный раздел!

Но… нельзя. Да, логи это уже какая-никакая отладка. Но мы можем получить логи длиной в несколько книжек.

Если вас устраивает такой расклад, то вы можете уже делать эмулятор, я вас не задерживаю (хотите, можете пролистать дальше информацию, а не задерживаться здесь).

Ну а для тех, кто всё же решил остаться, то давайте разберём, что же нам именно нужно?

  • Нам нужно видеть текущую инструкцию.
  • Нужно видеть память, с которой работаем (их может быть несколько разных).
  • Нужно видеть регистры, флаги, с которыми работаем.
  • Нужно уметь «ходить» по коду и исполнять его. И конечно же, видеть результат исполнения.

По сути это пока самое основное, что будет необходимо (логи тоже будут нужны — иногда они могут показать скрытую от глаз информацию). В дальнейшем же понадобится намного больше разной информации, которую мы можем видеть. Но я думаю, к тому времени вы уже сможете сами определиться, какая именно информация вам нужна для вывода.

Итак, вы должны будете сделать графическую оболочку, где будет отображаться различная информация по отладке. Посмотрите разнообразные IDE, которыми вы пользуетесь и которые используются в уже готовых эмуляторах и реализуйте то, что будет удобно для вас на начальном этапе. Не забывайте, пока вы развиваете свой эмулятор, вам с каждым шагом может понадобиться больше отладочной информации.

На это на всё уйдёт немало времени, но это сократит вашу дальнейшую разработку эмулятора. Итак, вы убили кучу времени на разработку процессора, потом убили кучу времени на разработку отладчика. Дальше вы потратите множество времени на отладку инструкций и состояний флагов, и глядишь месяц уже прошёл (если вы эмулируете сложный процессор, то может год и больше).

▍ Подготовительный этап пройден


Всё, теперь можете браться за видеоадаптер. Это же так интересно. Потратить на его изучение ещё кучу времени.

Если вы делаете эмулятор для устройства, которое уже было эмулировано, то можно посмотреть, как это делали люди, и сделать так же. Однако если вы так горите желанием, то вы вполне можете сами разработать свой способ эмуляции видеоадаптера (но всё же посмотрите, как это делали люди, лишним не будет). Лично я так и сделал и для Nes делаю свою эмуляцию PPU, но об этом не в этой статье.

После реализации видеоадаптера реализуйте управление. Если у вас будут готовы процессор, видеоадаптер и управление ко всему, то вы уже сможете более-менее полноценно протестировать ваш эмулятор.

А дальше уже по вашему выбору, что сами пожелаете (не забывайте про отладчик).

▍ Тесты


Будем основываться, что вы эмулируете какую-то приставку (допустим, Nes).

Помните, для тестов вам нужна программа. Иииии… зачастую лучше не готовые тесты для эмуляторов. Причина отказов от готовых тестов в том, что вы можете не понять, работает тест или нет, тест может не вывести информацию (ну вдруг ваш видеоадаптер ещё не работает или работает глючно). Но проверить их работоспособность стоит.

И потому для тестов вам надо брать простейшие игры. Как пример для Nes это игры 0-го маппера: Bomberman, Lode Runner, Battle City и другие.

В процессе разработки желательно запастись минимум десятком игр и желательно разным жанром, разным способом вывода данных на экран (скроллинг, вид сверху, вид сбоку, одноэкранные игры). Всё это пригодится в дальнейшем, а для начала надо взять игру и… постараться получить хоть какую-то информацию на экране (если у вас уже готово устройство вывода информации).

Как же можно проверить работоспособность видеоадаптера? Если рассматривать Nes, то там есть спрайты, фон и фон (смешно...). Да, там два фона, но один статичный — одноцветный (будем называть его задним фоном), а второй рисуется поверх него тайлами (будем называть его передним фоном). Так вот, задний фон заполняет весь экран Nes одним заданным цветом, а всё остальное рисуется уже поверх него. И вот этот фон как раз проще всего проверить. Вот берём и выводим задний фон:

// получаем цвет фона
backNesColor := Color_FindOrAdd_Single(Palette[videoRAM[$1f00] and $3F, 0], Palette[videoRAM[$1f00] and $3F, 1], Palette[videoRAM[$1f00] and $3F, 2], Palette[videoRAM[$1f00] and $3F, 3]);
// закрашиваем область экрана заданным цветом
pr2d_Rect(NesPPU.Screen, backNesColor, PR2D_FILL);

Правда очень сложно? Вроде бы нет, но это первый шаг к тому, что мы можем узнать, работает наш код или нет, просто хотя бы выводя фон.

Не забывайте тестировать эмулятор, особенно если всё уже готово для вывода видеоинформации! Тесты, тесты и ещё раз тесты! Это очень важно!

▍ Информация, которой нет


Если вы эмулируете устройство, которое популярно и уже часто эмулировалось, то, скорее всего, информация почти вся есть. А если её нет, то вы либо её не нашли, либо пропустили.

О! Это частая проблема! Вы читаете документацию, вроде бы всё учли — и вдруг бац: что-то работает не так, как надо. Приходится сидеть, просматривать работу инструкций, устройств и регистров иногда часами. А вот не было бы у вас отладчика, то просматривать бы пришлось ещё дольше (ну можете многотомник логов почитать и сравнить с работой соседского эмулятора).

Но есть ли информация, о которой практически не узнать, или её нет?

Тут палка о двух концах. Если вы идёте проторенной дорогой, то маловероятно, что вы столкнётесь с какими-то проблемами и сможете их заметить. За вас уже многое решили и реализовали. Вы повторили и забыли.

А вот если вы делаете сами… то тут начинаются проблемы. Эти проблемы может никто не описывал, может описывали на каком-то форуме и забыли (ну и не забываем, что банально мы сами могли пропустить информацию). В этом случае Да, вы столкнётесь с такой информацией. От этого никуда не убежать.

При реализации PPU я обнаружил, что Y смещён на 1 вниз. Ну где-то на просторах интернета написано что:

Sprite data is delayed by one scanline; you must subtract 1 from the sprite's Y coordinate before writing it here.

Данные спрайта задерживаются на одну строку развёртки; перед записью их сюда необходимо вычесть 1 из координаты Y спрайта.

И тут информация, которая будет противоречить реализованному мной PPU. В этой реализации Y надо наоборот увеличить (да, реализация PPU была сделана полностью мной, потому так и пишу от своего имени; подобных реализаций на данный момент я нигде не видел).

Абсолютно всю информацию вы не сможете сразу узнать, что-то выяснится в процессе тестирования эмулятора.

Например, в Nes приходит прерывание NMI, и как я понимаю, многие эмуляторы реализуют это прерывание таким способом: ловят прерывание на текущей инструкции, выполняют текущую инструкцию, выполняют следующую инструкцию и только затем начинают обрабатывать NMI. Хотя обрабатывать они должны сразу же после выполняемой инструкции, во время которой пришёл NMI. И без данной реализации эмулятор, что я делаю, зависает в определённых ситуациях (и другие эмуляторы, видимо, тоже зависали).

Если вы разбираетесь в радио/схемотехнике, то вы должны понимать, что программирование и работа в железе — это разные вещи. И то, что происходит в железе, не всегда можно точно эмулировать.

Какая-то информация «свалится с неба» (вы что-то увидите в процессе работы, подправите, и всё станет работать стабильно, а вот в документации может быть всё с точностью до наоборот, но не надо на это надеяться, это один шанс из миллиона).

Какую-то информацию вам смогут подсказать люди (я очень много информации «пропустил мимо ушей» пока изучал и люди чуть ли не носом тыкали меня в мои же ошибки или они могут подсказать, что и как работает, чего вы не знали).

Всегда держите под рукой документацию и смотрите информацию на сопутствующих форумах. Очень много информации на форумах просто теряется, но для разработки эмулятора она может быть полезной.

И вот ещё момент: все стараются гнаться за точностью эмуляции. Это правильно! Но не забываем, что мы делаем эмулятор! А эмулятор должен правильно выполнять запускаемые программы! Не важно как, важно, чтобы эти программы сами по себе точно работали на вашем эмуляторе. Вы же не симулятор писать собрались? Поэтому соблюдать точность нужно, но точность эмуляции может быть не критична (а может и критична!).

▍ Баги, и определённые реализации


Приготовьтесь к ним, к багам. Это ваши верные друзья на всё время написания эмулятора.

Думая, что вы правильно эмулировали какую-то часть эмулятора, при проверке вы можете поймать ещё десяток багов и потратить время на их исправление. Баги будут ждать вас на каждом углу, когда вы их ждёте и когда не ждёте.

Создание эмулятора — это путь проб и ошибок. Если вы написали эмулятор без единой ошибки, значит его писали не вы, а вы просто прошлись по чьим-то стопам и повторили всё (Плохо это? Да нет, не плохо, главное, чтобы знания вы получили, остальное вторично).

Если у вас пытливый ум, то для реализации определённых частей эмуляции вы приложите голову и решите, как по-своему можно реализовать тот или иной функционал. И неважно, что где-то вы подсмотрели подобное, важно, что вы вспомнили об этой возможности и воспользовались. Допустим в Nes есть «зеркальная» память — это память, которая повторяется друг за другом множество раз. Для её реализации можно выделить участок памяти, которая зеркалируется, а по остальным адресам указать ссылку на эту память (лично я, например, реализовывал отдельно участки памяти и ставил для всех областей указатели на эту память).

▍ Окончание


Какие минусы того, что вы будете реализовывать эмулятор:

  • вы потеряете много времени на изучение информации;
  • вы потеряете много времени на реализацию эмулятора (различных его частей);
  • на каждом шагу будете сталкиваться с новыми багами;
  • на каждом шагу будете исправлять свои же ошибки;
  • для отладки определённых моментов вас потребуются сутки-двое-трое-неделя;
  • вам нужно писать свой дебаггер.

Какие плюсы того, что вы будете реализовывать эмулятор:

  • вы почерпнёте много информации (возможно ненужной вам, тогда зачем брались?);
  • вы изучите устройство вдоль и поперёк, узнаете о его работе и работе его устройств, инструкций, регистров и прочего;
  • вы узнаете о многих скрытых багах;
  • вы узнаете, что вы тоже совершаете ошибки;
  • вы напишите отладчик;
  • вы получите удовлетворение, что у вас получается задуманное и другие могут попробовать результат вашего труда.

Надеюсь, я сумел отговорить вас от создания собственного эмулятора!?

▍ Постскриптум


Можно долго и упорно писать статью, о том, что вас ждёт, какие ошибки вы можете совершить, какую информацию изучать. Но по сути надо донести основу: Решили делать! И хотите делать! Делайте! Остальное не так важно.

С чем лично я сталкивался? Я портировал эмулятор на Android и запустил его. Результат меня подразочаровал…

Ютуб


Рутуб


Лично я надеялся, что мне удастся хотя бы в 40 кадров уложиться, но эмулятор и до 30 кадров не дотянул.

На текущий момент я исправил эту проблему и довёл до 60 кадров с мелкими просадками. В данное время этого вполне хватит, но надо будет ещё улучшать эту часть кода. И проблема была не только в самом эмуляторе, проблема была и в разрабатываемой мной библиотеке ZenGL (я продолжатель). Андрей сделал возможность сборки приложений под Android и реализовал вывод посредством эмуляции glBegin/glEnd и это было давно (2011 или 2012-й года), а я использовал эту функциональность для вывода. Избегая этой функциональности, я ускорил вывод кадра на 3-4 (притом, что до этого я вообще ни на кадр сдвинуться не мог). Но переделывать PPU пришлось полностью, чтобы избавиться от основной проблемы — частого переключения текстур (видимо, на мобильных системах это немалая проблема).

Также была проблема с выводом спрайтов/тайлов. Эта проблема, по сути, требует большого разбора. Если в двух словах, то надо читать документацию, узнавать, как всё работает. Не забывать, как выводятся большие спрайты и куча прочей мелочёвки, которую надо учитывать (но получилось всё равно прикольно).

Ютуб


Рутуб


Большинство недочётов я изначально хотел отложить подальше, так как считал, что не очень они важны, но часть из них говорит об обратном )))

А в следующей статье я глядишь распишу, как я создавал свой PPU для Nes. Ну и постараюсь там побольше технической информации предоставить.

Успехов вам в ваших начинаниях! И терпения, если вы всё же решились сделать свой эмулятор.

Контакты для связи со мной
Ютуб канал
Рутуб канал
Я на gamedev.ru
телега: @SeenkaoSerg

© 2025 ООО «МТ ФИНАНС»

Telegram-канал со скидками, розыгрышами призов и новостями IT ?

Комментарии (0)