Передовица » Эмуляторы/утилиты » Агат-Free (FreeBSD & Linux)

Агат-Free (FreeBSD & Linux)

Эмулятор Агат-Free или Инженерный эмулятор. Основные задачи разработки: оптимизация кода для современных i386-процессоров (NetBurst / Core Duo / Core Solo...), перенос под *NIX-совместимые системы, а также расширение возможностей мониторинга виртуальных устройств.

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

Главная задача эмулятора: анализ агатовских программ. Этакий эмулирующий отладчик. Поэтому, едва ли не бОльшую часть кода составляют части ("мониторы"), позволяющие наблюдать за поведением виртуальных блоков Агата. С другой стороны - мне было интересно выжать максимум производительности из виртуального процессора и виртуальной памяти, поэтому некоторые ограничения ДОС-овской версии всё же были сохранены: например, только одна break point для указателя команд и одна - для перехвата чтения/записи данных.

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

Этот эмулятор распространяется свободно (внутри LiveCD, в скомпилированном виде), но исходники не опубликованы на сайте. Если хотите их получить - напишите мне.

Архитектура и требования к операционной системе

Код эмулятора разделён на две части, почти точно соответствующие двум тредам. Тред виртуального процессора и шинных интерфейсов устройств почти полностью написан на ассемблере и компилируется gas. Благодаря глубокой оптимизации скорость удалось поднять примерно в 4-5 раз (по сравнению с ДОС-версией). Код мониторов написан на C и компилируется gcc. ОС - FreeBSD, хотя, возможно, будет работать и под Linux и под другими *BSD-системами: главное, чтобы были нужные библиотеки и ЦП не ниже Pentium-IV.

Единственное (если ОС не FreeBSD): эмулятор довольно жестко привязан к однобайтной кодировке символов и в некоторых случаях принятая в системе UTF может где нибудь вылезти боком. Но этого может и не случиться. Во всяком случае, если не использовать русских имён файлов. И в консольке указать индивидуальную кодировку КОИ-8, тем более, что это, обычно, не сложно.

Используемые библиотеки: libc, libpthread, libcurses. Виртуальный дисплей может отображать текстовые режимы в curses-окно, и, одновременно, в X11 (если доступно). Поэтому нужны также libX11, libXext и поддержка MIT-SHM со стороны X11 (вроде в нынешних версиях она уже встроенная).

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

Доводы против X11: 1) мой код рендера картинки очень не оптимален и заметно грузит процессор. 2) удалённая работа не будет требовать толстого канала.

Оба треда исполняются почти асинхронно и можно одновременно видеть, например, меняющееся изображение на виртуальном дисплее и изменения в виртуальной памяти, регистрах ЦП и т.д.

Эмулятор может воспроизводить звук, хотя и с рядом ограничений. Для этого используется интерфейс Open Sound System (4front). Драйвера стандарта ALSA, используемые в Linux, могут общаться с программами по стандартам OSS, но этот режим должен быть разрешен при компиляции ядра.

Производительность и требования к аппаратуре

Тред виртуального процессора в некоторых местах использует префиксы команд ветвления, которые, согласно интеловской документации, относятся к группе SSE2. Однако, практика показывает, что эмулятор нормально работает даже на Pentium-III, который SSE2 не поддерживает. Почему так происходит - не знаю. SSE2 поддерживается почти всеми Penitum-IV, таким образом, этот процессор можно считать минимально необходимым для эмулятора. Но так как эмулятор использует два треда, его потенциал должен полностью раскрываться только на двухядерных процессорах. Ну или хотя бы на HyperThread. Но это только если хочется получить предельную производительность.

Тест на Pentium-IV, одно ядро, без HT, 2.4 ГГц. Гоняём тест памяти девятки Лисина:

  • ДОС-эмулятор на предельной скорости: 6 проходов за ~45 секунд.
  • AgatF, без тормозов, без X11: 6 проходов за 10 секунд.
  • AgatF со стабилизацией скорости: 6 проходов примерно за 11 минут. Предположим, что это соответствует реальному Агату.

Отсюда можно оценить эквивалент в попугаях: ДОС = агатовским 15 МГц, AgatF = 66 МГц.

Если же ограничить скорость работы виртуального ЦП, привязав её к 1 МГц (реальная частота ЦП Агата - 14.3 МГц / 14 ~= 1.02 МГц), на том же P-IV: эмулятор (без использованя X11) с трудом потребляет около 1 процента времени ЦП (при этом колебания нагрузки составляют тот же процент, так что значение получается не особенно точным). Это измерение делалось на FreeBSD 7.0.

Требования к физическому ОЗУ невелики: у меня, например, top показывает около 8 Мб полного адресного пространства и 4 Мб - физической памяти. Т.е. почти ничего :)

Об ia-64

Мощно напрягая мозги я пытался решить: будет ли польза эмулятору от 64-разрядных процессоров ? Вероятно, не будет. Я даже слабо представляю, к какому месту в нём привязать SIMD-команды (MMX*, SSE*...), которых море и на 32-разрядниках. Разве что к поиску (перебору) точек остановки (если их число увеличить). Может быть, можно было бы использовать здоровенные xmm-регистры как кеш виртуальной памяти... Но пока не верится, что это сильно поднимет скорость (с учётом и так наличествующующих физических кешей).

В общем-то - для кода мониторов - SIMDы бы пригодились, но этот тред написан на C, где я стараюсь всю работу свалить на имеющиеся библиотеки, так что пусть об этом думают разработчики X11 (тут хорошо видно место для SIMD).

Что касается самой возможности использовать ia64 в режиме совместимости, то, по крайней мере для FreeBSD, это вроде как возможно, при наличии "options COMPAT_IA32" в конфиге ядра и установленных /usr/lib32, но мне не на чем пробовать.

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

Виртуальные устройства и конфигурирование

Эмулятор не имеет жестко заданных вариантов исполнения - 7 или 9. Каждое устройство (кроме ЦП и системной шины) может быть отключено. Это касается даже клавиатуры, памяти (в т.ч. базовой) и т.д.. Каждое подключенное устройство, если оно имеет различные варианты в различных моделях ПЭВМ, может быть сконфигурировано независимо.

Единственное ограничение: каждое устройство может быть подключено только в одном экземпляре.

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

Например: "./AgatF -c 9 -v FDD800.imgname0=sys.img" - запускает эмулятор, все устройства которого будут сконфигурированы в соответствии с секцией [9], соответствующей стандартной девятке, с подключенным образом диска sys.img.

Можно строить множество конфигураций, которые будут отличаться чем угодно, включая используемые шрифты, палитры, раскладки клавиатуры, скорости работы, системные мониторы и т.д.. Есть возможность вложения секций: т.е. можно описать, например, базовую машину Агат-7, а затем - на её основе - вариант этой машины без пультов и 840 кб дисковода, в дисководе 140 кб которой сразу установлен диск с Lode Runner (некоторые версии LR "виснут" при подключенных пультах): "./AgatF -c 7lr".

Центральный процессор

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

Наблюдать за работой ЦП можно, видя историю исполненных команд, значения регистров, фрагмент стека, на который указывает SP, а также прозвольно выбранный участок адресного пространства (не обязательно ОЗУ, можно видеть и область $Cxxx, но в этом случае возможна реакция соответствующих устройств).

Можно редактировать значения регистров, останавливать/запускать ЦП, проходить одну команду за шаг. Исполняемые команды можно выводить как по порядку исполнения, так и по порядку адресов.

Есть возможность выполнить запись нуля по любому адресу: это бывает полезно для воздействия на периферийные устройства как бы от имени ЦП.

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

Память

Эмулируется базовая память семерки в варианте 32 Кб и девятки в варианте 128 Кб, а также модули дополнительной памяти семёрки - ДопОЗУ и ЭмПЗУ. Все блоки памяти доступны для просмотра и редактирования, даже если в данный момент они не подключены к адресному пространству ЦП. Просмотр: как в виде hex-дампа так и в виде дизассемблированных команд. Доступны для просмотра и модификации управляющие регистры.

Мониторов памяти два: один - для базовой памяти, второй - для модулей ДопОЗУ и ЭмПЗУ.

Я не знаю, как будут взаимодействовать модули ДопОЗУ и ЭмПЗУ семёрки с базовой памятью девятки, поэтому если выбрана архитектура базовой памяти - девятка, модули семёрки блокируются, вне зависимости от того, разрешены они или нет.

В отличие от ДОС-версии, первоначальное содержимое памяти - не мусор, а нули. Это не мой злой умысел, а требование *NIX по безопасности: очередная программа не должна знать, чем до неё занималась другая программа. Известно, что для Агата существовало некоторое количество глюковатых программ, которые могли зависеть от первоначального состояния памяти: здесь они будут вести себя более стабильно: или стабильно работать или стабильно не работать :). Но для экспериментальных целей я сохранил возможность заполнения памяти любой 32-битной величиной (в любой момент).

Дисплей

Эмулирует все режимы в окне X11 и, одновременно, все текстовые режимы в окне curses (в этом случае текст можно копировать в буфер обмена, также как в ДОС-овской версии). В X11 можно использовать различные варианты шрифтов и палитр. Можно задавать ширину символов (до 8 пикселей). Высота автоматически расчитывается исходя из размера файла шрифта и не имеет явных ограничений.

В отличие от ДОС-овской версии поддерживаются все цветовые особенности в зависимости от состояния регистра палитр; режим Т64 меняет фон/цвет так, как это было в настоящем Агате. Однако, подобно ДОС-версии, не поддерживается режим мерцания.

Для всех X11-режимов можно задавать целочисленные коэффициенты масштабирования по осям, шрифты для Т32/Т64/Apple Text могут быть различными (как матрица так и размер, за исключением Apple Text: так как этот же шрифт используется в режиме Apple Mix, его размер должен быть 7x8 точек).

Клавиатура

В curses поддерживается ввод в раскладке qwerty. Curses не делает различий между клавишами основного блока и numpad, поэтому, чтобы эмулировать ф-блок Агата, введён дополнительный переключатель раскладок.

В X11 есть возможность полноценного анализа физической клавиатуры, поэтому реализованы все те же возможности, что и в ДОС-версии, но таблица преобразований вынесена во внешний текстовый файл, который можно свободно редактировать. Он содержит семь колонок: имя физической клавиши и шесть кодов, которые будут переданы в виртуальный порт при нажатии этой клавиши отдельно, совместно с Shift и совместно с Ctrl, для двух возможных раскладок: jcuken и qwerty.

Дисководы

Поддерживаются все агатовские форматы образов: DSK, NIB/IM, AIM. По сравнению с ДОС-версий переработана логика эмуляции 140 кб дисководов. Теперь она гораздо точнее, хотя я не уверен, что это будет важно реальному софту. Несмотря на эти изменения, прежняя концепция с постоянной готовностью дисковода (т.е. бит готовности всегда равен единице и драйвер может читать данные в любой момент) была сохранена (и 140 и 800). С одной стороны - это не позволяет корректно работать программам измерения скорости дисковода, с другой - увеличивает скорость.

Для DSK-образов учитываются возможные файлы-спутники: .seq, .extra/.ext, .vol, однако при форматировании DSK из виртуальной машины эти файлы не будут модифицироваться.

Как и в ДОС-овской версии, файлы образов открываются только на время чтения/записи трека, поэтому можно обрабатывать образ, который подключен эмулятором. Более того, если изменить формат образа, даже не меняя его имени - эмулятор верно учтёт эти действия (редкое исключение: смена формата с AIM на DSK при наличии модифицированного буфера: в этом случае декодер, записывающий DSK, может не справится со сложноформатированным исходным AIM-треком).

Недоступность файла для чтения (включая отсутствие файла) не вызывает ошибок, виртуальная машина в этом случае получит поток 0x80 или 0x00. Т.е. можно, например, удалить файл, потом создать его снова, не закрывая эмулятор. Как только файл будет доступен - виртуальная машина получит доступ к нему.

Блокировка записи может быть выставлена пользователем явно либо определяться операционной системой. Но, в отличие от ДОС-версии, здесь проверяется не наличие флага Read Only (которого в *NIX всё равно нет), а фактическая доступность файла (функцией access(2)).

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

Таймерные прерывания

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

Звук

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

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

В реальных программах получается так: игрушки с коротким похрюкиванием продолжают уверенно хрюкать, когда ЦП привязан к реальному времени. Проги, воспроизводящие музыку - вроде jukebox и комплексного теста - чисто и красиво поют, если ЦП отстегнуть от реального времени. Ну а всё промежуточное будет вести себя не вполне адекватно. Но таких программ на Агате немного.

Пульты/мыши

Эмулируются обычные пульты, а также мышь-марсианка и мышь с контроллером Nippel card. Но всё это доступно только при поддержке X11.

FIL-загрузчик

Это виртуальное устройство позволяет сразу при "включении" машины загрузить B-файл из FIL-контейнера и передать на него управление.

Такое решение может быть использовано для запуска некоторых программ, в основном - крупных B-игр, которые не требуют наличия в памяти операционной системы. В этом случае, используя FIL-загрузчик, можно запустить игру, не создавая образ диска: "./AgatF -c 9 -v FILoader=rise.fil".

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

Загрузка может быть выполнена как в регион эмулятора ПЗУ, так и в базовую или дополнительную память. Базовая память (даже если не используется) должна быть разрешена. Если загрузку требуется выполнить в дополнительную память - проверяется её доступность и корректируется нужный регистр управления. То же происходит, если загрузка выполняется в эмулятор ПЗУ (но включение ЭмПЗУ приводит к отключению системного монитора !).

Решения об использовании тех или иных банков памяти зависят от точки загрузки и размера файла: эта информация хранится в FIL-контейнере. Перед загрузкой все регионы памяти заполняются константой $60 - код команды RTS.

Для ещё большего удобства была написана пара программ: специальный стартёр для Бейсик-60, который может запустить бейсик из плоского образа (без ДОС в памяти), а также линкер, который может согнать в один FIL-контейнер Бейсик, стартёр и любую бейсиковскую программу. Подсунув такой контейнер эмулятору можно сразу увидеть исполнение Бейсик-программы (минуя команды типа RUN, всевозможные игротеки-запускалки и прочее). Это удобно: просто щёлкнув на FIL-контейнер с бейсик программой сразу увидеть её в работе.

Ограничения FIL-загрузчика очевидны: так как нет никакого образа диска и операционной системы, программа внутри виртуальной машины не может дочитывать и записывать другие файлы в процессе работы. Однако виртуальные дисководы могут использоваться программой, если она содержит полный драйвер файловой системы и дисковода. Например, можно загрузить из FIL-контейнера самораспаковывающийся образ операционной системы с бейсиком и за секунду получить полностью загруженную виртуальную машину. Это удобно, например, при исследовании дисков Apple ][: FIL-контейнер содержит всё необходимое для перевода Агат-9 в режим эмуляции Apple ][, а в 140к дисководе уже стоит исследуемый образ. PR#6

Расширенный дизассемблер

Помимо простых HEX- и дизассемблированных дампов памяти эмулятор может предложить расширенный дизассемблер. Основное отличие: назначаемые пользователем имена адресов - символьные метки. Можно сохранить массив меток и вернутся затем к нему вновь.

Все данные видны так, как их видит ЦП. Можно ставить метки в регионе $Cxxx, но данные этого региона не запрашиваются и, следовательно, не отображаются.

Подобно досовскому эмулятору можно сохранить дамп памяти в файле (для анализа более мощными средствами), но теперь есть два вида дампов: монитор ЦП сохраняет дамп 64кб - как его видит эмулируемый ЦП, а мониторы памяти могут сохранять дампы своих блоков - как они хранятся в эмулируемой памяти.

Использование материалов проекта agatcomp без получения предварительного письменного разрешения agatcomp запрещено.


Почта для обратной связи: mail@agatcomp.ru


Живое общение по теме Агата: Telegram группа Agatcomp.


Накопленные знания и проекты: тематический ФОРУМ.


© 2004-2024 agatcomp.su / agatcomp.ru

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *