Как работают эмуляторы и как они написаны?

Как работают эмуляторы? Когда я вижу эмуляторы NES/SNES или C64, меня это поражает.

http://www.tommowalker.co.uk/snemzelda.png

Нужно ли имитировать процессор этих машин, интерпретируя его конкретные инструкции по сборке? Что еще входит в это? Как они обычно оформлены?

Можете ли вы дать какой-нибудь совет тем, кто заинтересован в написании эмулятора (особенно игровой системы)?

16 ответов

Решение

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

Основная идея:

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

Эмуляция процессора:

Существует три способа обработки эмуляции процессора:

  • интерпретация
  • Динамическая перекомпиляция
  • Статическая перекомпиляция

Со всеми этими путями у вас одна и та же общая цель: выполнить фрагмент кода, чтобы изменить состояние процессора и взаимодействовать с "оборудованием". Состояние процессора - это конгломерат регистров процессора, обработчиков прерываний и т. Д. Для заданной цели процессора. Для 6502 у вас будет несколько 8-битных целых чисел, представляющих регистры: A, X, Y, P, а также S; у вас также будет 16-битный PC регистр.

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

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

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

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

Все это делает статическую перекомпиляцию совершенно невозможной в 99% случаев. Для получения дополнительной информации Майкл Стейл провел большое исследование статической перекомпиляции - лучшее, что я видел.

Другая сторона эмуляции процессора - это способ взаимодействия с оборудованием. Это действительно имеет две стороны:

  • Время процессора
  • Обработка прерываний

Время процессора:

Некоторые платформы - особенно старые консоли, такие как NES, SNES и т. Д. - требуют, чтобы ваш эмулятор имел строгую синхронизацию, чтобы быть полностью совместимым. С NES у вас есть PPU (блок обработки пикселей), который требует, чтобы процессор вставлял пиксели в свою память в точные моменты. Если вы используете интерпретацию, вы можете легко считать циклы и эмулировать правильное время; с динамической / статической перекомпиляцией все становится намного сложнее.

Обработка прерываний:

Прерывания - это основной механизм взаимодействия ЦПУ с оборудованием. Как правило, ваши аппаратные компоненты сообщают процессору, о каких прерываниях он заботится. Это довольно просто - когда ваш код генерирует данное прерывание, вы смотрите на таблицу обработчиков прерываний и вызываете правильный обратный вызов.

Аппаратная эмуляция:

Есть две стороны для эмуляции данного аппаратного устройства:

  • Эмуляция функциональности устройства
  • Эмуляция реальных интерфейсов устройства

Возьмите случай с жестким диском. Функциональность эмулируется путем создания резервного хранилища, процедур чтения / записи / форматирования и т. Д. Эта часть, как правило, очень проста.

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

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

Ресурсы:

Я думаю, что я дал довольно хорошее введение здесь, но есть тонна дополнительных областей. Я более чем рад помочь с любыми вопросами; Я был очень расплывчат в большей части этого просто из-за огромной сложности.

Обязательные ссылки Википедии:

Общие ресурсы эмуляции:

  • Zophar - это то место, где я начал с эмуляции, сначала загрузив эмуляторы и в конце концов разграбив их огромные архивы документации. Это самый лучший ресурс, который вы можете иметь.
  • НГему - Немного прямых ресурсов, но их форумы непобедимы.
  • http://www.romhacking.net/ - раздел документов содержит ресурсы, касающиеся архитектуры машин для популярных консолей

Эмулятор проектов по ссылке:

  • IronBabel - это платформа эмуляции для.NET, написанная на Nemerle и перекомпилирующая код в C# на лету. Отказ от ответственности: это мой проект, так что извините за бесстыдную вилку.
  • BSnes - потрясающий эмулятор SNES с идеальной точностью цикла.
  • MAME - аркадный эмулятор. Отличная ссылка.
  • http://6502asm.com/ - это эмулятор JavaScript 6502 с классным небольшим форумом.
  • dynarec'd 6502asm - это небольшой взлом, который я сделал за день или два. Я взял существующий эмулятор с 6502asm.com и изменил его, чтобы динамически перекомпилировать код в JavaScript для значительного увеличения скорости.

Рекомендации по перекомпиляции процессора:

  • Исследование статической перекомпиляции, выполненное Майклом Стейлом (ссылка выше), завершилось в этой статье, и вы можете найти источник и тому подобное здесь.

Приложение:

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

Пожалуй, самая захватывающая вещь в эмуляции сейчас - это libcpu, созданная вышеупомянутым Майклом Стейлом. Это библиотека, предназначенная для поддержки большого количества ядер ЦП, которые используют LLVM для перекомпиляции (статической и динамической!). У этого есть огромный потенциал, и я думаю, что он сделает большие вещи для эмуляции.

На мое внимание также было обращено внимание на emu-docs, в котором находится отличное хранилище системной документации, что очень полезно для целей эмуляции. Я не провел там много времени, но, похоже, у них много отличных ресурсов.

Я рад, что этот пост был полезен, и я надеюсь, что смогу сорваться с задницы и закончить свою книгу по этому вопросу к концу года / началу следующего года.

Парень по имени Виктор Мойя дель Баррио написал свою диссертацию на эту тему. Много полезной информации на 152 страницах. Вы можете скачать PDF здесь.

Если вы не хотите регистрироваться в scribd, вы можете найти в PDF название Google "Изучение методов эмуляции программирования". Есть несколько разных источников для PDF.

Эмуляция может показаться пугающей, но на самом деле она гораздо проще, чем симуляция.

Любой процессор обычно имеет хорошо написанную спецификацию, которая описывает состояния, взаимодействия и т. Д.

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

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

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

Учитывая очень низкую производительность старых видеоигр (NES/SNES и т. Д.), В современных системах эмуляция довольно проста. На самом деле, еще более удивительно, что вы можете просто загрузить набор всех игр SNES или любой игры Atari 2600, учитывая, что, когда эти системы были популярны, свободный доступ к каждому картриджу был бы мечтой.

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

Тем не менее, существует очень известное исключение из этого, называемое "UltraHLE" ( статья в WIKIpedia). UltraHLE, один из самых известных когда-либо созданных эмуляторов, эмулировал коммерческие игры Nintendo 64 (с приличной производительностью на домашних компьютерах) в то время, когда многие считали, что это невозможно. Фактически, Nintendo все еще производила новые названия для Nintendo 64, когда был создан UltraHLE!

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

Концепция UltraHLE заключалась в том, чтобы сделать невозможное возможным путем эмуляции вызовов библиотеки C вместо вызовов на уровне машины.

Стоит обратить внимание на попытку Имрана Назара написать эмулятор Gameboy на JavaScript.

Создав свой собственный эмулятор микрокомпьютера BBC 80-х годов (напишите в Google VBeeb), есть несколько вещей, которые нужно знать.

  • Вы не подражаете реальной вещи как таковой, это будет копия. Вместо этого вы подражаете государству. Хороший пример - калькулятор, в действительности у него есть кнопки, экран, чехол и т. Д. Но для эмуляции калькулятора вам нужно только эмулировать, находятся ли кнопки вверх или вниз, какие сегменты ЖК-дисплея и т. Д. В основном, набор цифр представляя все возможные комбинации вещей, которые могут измениться в калькуляторе.
  • Вам нужен только интерфейс эмулятора, чтобы он появлялся и вел себя как настоящий. Чем убедительнее это, тем ближе эмуляция. То, что происходит за кулисами, может быть чем угодно. Но для простоты написания эмулятора существует ментальное отображение, которое происходит между реальной системой, то есть чипами, дисплеями, клавиатурами, печатными платами и абстрактным компьютерным кодом.
  • Чтобы эмулировать компьютерную систему, проще всего разбить ее на более мелкие порции и эмулировать эти порции по отдельности. Затем соедините все вместе для готового продукта. Очень похоже на набор черных ящиков с входами и выходами, которые прекрасно подходят для объектно-ориентированного программирования. Вы можете далее подразделить эти куски, чтобы облегчить жизнь.

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

Да, вы должны интерпретировать весь беспорядок двоичного машинного кода "вручную". Мало того, в большинстве случаев вам также приходится моделировать какое-то экзотическое оборудование, которое не имеет эквивалента на целевой машине.

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

При разработке эмулятора вы интерпретируете сборку процессора, на которой работает система (Z80, 8080, процессор PS и т. Д.).

Вам также необходимо эмулировать все периферийные устройства, которые есть в системе (видеовыход, контроллер).

Вы должны начать писать эмуляторы для систем simpe, таких как старый добрый Game Boy (использующий процессор Z80, я не ошибаюсь) ИЛИ для C64.

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

Пример этого см. По http://queue.acm.org/detail.cfm?id=1755886.

Это также покажет вам, почему вам нужен мульти-ГГц процессор для эмуляции 1 МГц.

Также посетите сайт http://emulators.com/ Дарека Михока (Darek Mihocka), где вы найдете отличные советы по оптимизации уровня команд для JIT и многие другие полезные советы по созданию эффективных эмуляторов.

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

Shared Source Device Emulator содержит встроенный исходный код для эмулятора PocketPC/Smartphone (требуется Visual Studio, работает в Windows). Я работал над V1 и V2 бинарного релиза.

Он решает многие проблемы эмуляции: - эффективный перевод адресов с гостевого виртуального на гостевой физический на виртуальный хост - компиляция гостевого кода JIT - моделирование периферийных устройств, таких как сетевые адаптеры, сенсорный экран и аудио - интеграция пользовательского интерфейса, для клавиатуры и мыши хоста - сохранение / восстановление состояния, для симуляции резюме из режима пониженного энергопотребления

Советы по эмуляции реальной системы или вашей собственной вещи? Я могу сказать, что эмуляторы работают, эмулируя все оборудование. Может быть, не в цепи (как перемещение битов, как HW сделал бы. Перемещение байта является конечным результатом, поэтому копирование байта в порядке). Эмулятор очень сложно создать, так как вам нужно смоделировать множество хаков (например, необычных эффектов), проблем с синхронизацией и т. Д. Если один (входной) фрагмент неправильный, вся система может потерпеть неудачу или в лучшем случае иметь ошибку / сбой.

Добавить ответ, предоставленный @Cody Brocious
В контексте виртуализации, когда вы эмулируете новую систему (ЦП, В / В и т. Д.) Для виртуальной машины, мы можем видеть следующие категории эмуляторов.

Интерпретация: bochs является примером интерпретатора, это эмулятор ПК для x86, он берет каждую инструкцию из гостевой системы, переводит ее в другой набор инструкций (ISA хоста) для получения желаемого эффекта. Да, это очень медленно, не кэшируйте ничего, поэтому каждая инструкция проходит один и тот же цикл.

Динамический эмулятор: Qemu - динамический эмулятор. Он выполняет на лету перевод гостевой инструкции, а также кеширует результаты. Лучшая часть заключается в том, что как можно больше команд выполняется непосредственно в хост-системе, что ускоряет эмуляцию. Также, как упомянул Коди, он делит код на блоки ( 1 поток выполнения).

Статический эмулятор. Насколько я знаю, нет статического эмулятора, который мог бы помочь в виртуализации.

Я написал статью об эмуляции системы Chip-8 в JavaScript.

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

Я скоро напишу более длинное руководство для NES.

Как бы я начал эмуляцию.

1. Получите книги, основанные на низкоуровневом программировании, они понадобятся вам для "притворной" операционной системы Nintendo... game boy...

2.Получить книги по эмуляции и, возможно, по разработке. (вы не будете делать ОС, но ближе всего к нему.

3. взгляните на некоторые эмуляторы с открытым исходным кодом, особенно те, для которых вы хотите создать эмулятор.

4. скопируйте фрагменты более сложного кода в ваш IDE/ компилятор. Это избавит вас от написания длинного кода. Это то, что я делаю для разработки ОС, использую район Linux

Другие вопросы по тегам