Inferno #08
30 ноября 2005

Sofтинка - Эмулятор ZX Spectrum на ZX Spectrum.

          ZXZXEmul
   Продолжая  тему шутки, опубликованной в
ACNews#29 (на самом деле идея ещё старше), 
я  решил  написать эмулятор ZX Spectrum на 
ZX Spectrum. Ничего невозможного тут нет,в 
методах тоже нет ничего необычного. По су─ 
ти,эмулятор заменяет собой медленную трас─ 
сировку, которая бывает в отладчиках,и сам 
может стать ядром такого отладчика (конеч─ 
но, не  в теперешнем виде, который слишком 
ориентирован на скорость).При этом имеется 
важное  удобство - в ПЗУ можно подставлять 
свою прошивку,а распределение памяти можно 
взять  вообще от другой модели компьютера. 
И можно, скажем, эмулировать  мышь  при её 
отсутствии. Другое  полезное свойство эму─ 
лятора,достижимое в обычных отладчиках то─ 
лько  при  перешивании ПЗУ (трассировку не 
считаем, т.к.гонять сложную программу,име─ 
ющую пользовательский интерфейс, под трас─ 
сировкой невозможно) - точки останова мож─ 
но  укладывать  в один байт. При этом стек 
программы не портится (это уже не достижи─ 
мо в обычных отладчиках). 

   Эмулятор пишется следующим образом.

   Сначала  отлаживаем  систему команд при
ОЗУ 16k и имеющемся ПЗУ.(Этот режим удобен
тем, что при эмуляции не нужно переключать
странички.)  Команды выбираются по таблице
(для каждого кода команды - адрес подпрог─
раммы),при этом указатель на текущую кома─
нду  хранится в DE. Основной набор регист─
ров удобнее всего хранить в альтернативном
наборе, а  основные A, BC, HL оставить для
разного рода вычислений.Можно было бы раз─
местить  в регистрах  и эмулируемый SP, но
выигрыш,видимо,будет очень небольшой. Зато
весьма  упрощает задачу использование IX в
качестве текущего индексного регистра. (Не
текущий  индексный  регистр  должен лежать
где-то  в переменной в памяти, равно как и
регистры альтернативного набора.)
   Команды  с префиксами расписаны в отде─
льных таблицах. При смене текущего индекс─
ного префикса индексные регистры обменива─
ются (IX<->память) . Этим убилось 2 зайца:
IY можно занять под что-то полезное; нужен 
всего  один набор выполнялок индексных ко─
манд.

   Эмулятор, естественно,с первого раза не
заработал, хотя  был  написан всего за два
дня, с использованием  макросов (чтобы был
шанс  быстро  заменить  по  всей программе
глючный фрагмент, если он будет найден).
   Отладка происходила так.
   Эмулируем сброс. Видим, что он не рабо─
тает. Идём  по  прошивке ПЗУ шаг за шагом,
останавливая  Unreal Speccy условными точ─
ками  останова (pc==nnnn)&&(de==mmmm), где
nnnn - адрес  начала  выбиралки команды, а 
mmmm - адрес  в  ПЗУ, на  котором мы хотим 
остановиться.Остановив,списываем регистры.
Запускаем второй экземпляр Unreal Speccy и
смотрим, какие в этой точке ПЗУ в действи─
тельности должны быть регистры.Параллельно
изучаем  маршрут, по которому наш эмулятор
выполняет ПЗУ (записываем все адреса ветв─
ления). Если видим, что наш эмулятор пошёл
не туда, куда надо (а куда надо, нам пока─
зывает второй экземпляр Unreal Speccy ),то
это именно тот случай,когда надо проверить
регистры. Заодно  проверяем  эмулированные
системные переменные.

   Чтобы  дойти до сообщения "Sinclair Re─
search Ltd", в нашем эмуляторе даже не ну─ 
жно эмулировать прерывания.Однако эмулиро─
вать прерывания так или иначе понадобится.
   Для  начала была сделана "честная" эму─
ляция IM1 (пока без IM2) с настоящим выпо─
лнением каждой команды обработчика.До вхо─
да  в обработчик (то есть  до эмуляции RST
#38 ) нужно  делать  две  вещи:  проверить 
эмулируемый  триггер разрешения прерываний
(заодно  сбросив его) и закончить эмуляцию
текущей эмулируемой команды.Для того,чтобы
закончить эмуляцию текущей эмулируемой ко─
манды, мы  просто ставим перехват в начале
выбиралки команды.
   Позже  обработчик прерываний был услож─
нён: появилось IM2, а для IM1 была сделана
возможность вместо "честной" эмуляции под─
ключить  вызов настоящего #38. Это сущест─
венно  быстрее.  Естественно, перед  таким
вызовом проверяется, там ли  IY, а если не
там, то  нужно  "честно" эмулировать глюки
эмулируемой программы :)

   Ограничение памяти (16k вместо 48k) бы─
ло сделано очень просто:запрещена запись в
ячейки выше #7fff.

   В конце концов (на пятый день) эмуляция
16k  заработала, и  в  эмулируемом бейсике
стало возможно выполнять операторы.
   Следующий шаг - 48k. Для него понадоби─
лись  макросы  работы с памятью. Эмулятору
 нужны операции с памятью:
  1. взятие  кода  команды  или  параметра
 команды; 
   2. запись в виртуальное ОЗУ;
  3. чтение из виртуального ОЗУ;
и др. Перечисленные - основные.Виртуальное
ОЗУ  размещается в верхних страницах памя─
ти.
   Написав такие макросы, я получил возмо─
жность организовать перехват вывода на эк─
ран:если эмулируемая pg5 лежит не в реаль─
ной  pg5  (это переключается в исходнике -
попробуйте),то команды записи на экран мо─
жно  перенаправлять и туда, и туда, причём
на реальный  экран можно писать не всегда,
а по какому-то условию (для примера я реа─
лизовал  отсечение  правой  части экрана -
там можно разместить, скажем, какие-то ин─
дикаторы).
   После  отладки 48k (заодно была отрабо─
тана возможность смены прошивки ПЗУ) я пе─
решёл  к 128k. Различие здесь уже не такое
принципиальное,как между 16k и 48k. Просто
нужно  добавить обработку OUT (#fd) и реа─
лизовать в эмуляторе соответствующую пере─
менную. Точнее,переменных нужно две: какая
страница  ОЗУ включена с точки зрения эму─
лируемой  программы  и какую на самом деле
нужно   включать  при  работе  с  адресами
>=#c000. 
   Для  отладки 128 бейсика нужно реализо─
вать  и переключение страниц ПЗУ. Я заодно
реализовал   и  возможность   переключения
страниц в окнах #4000 и #8000, для будущей
эмуляции  АТМ. Это потребовало лишь замены
LD A,константа на LD A,(переменная) и пра─ 
ктически не замедлило эмуляцию.

   48 бейсик  работал, несмотря на то, что
некоторые команды эмулировались неправиль─
но. Погоняв  128 бейсик, а  также  Mr Gluk
Reset Service, я  протестировал  остальные 
команды.Не проверенными остались недокуме─
нтированные команды с индексно-регистровой
адресацией, но я их и не реализовывал.

   Обратите  внимание, что  при включенном
128 бейсике  не  должен  работать  выход в
TR-DOS  по  #3dxx! Иначе  не считает каль─
кулятор 128 бейсика! Я это нашёл не сразу,
т.к. на моём  реальном пентагоне #3dxx ра─
ботает  и при включенном 128 бейсике. Точ─
нее, #3dxx  у  меня в этом случае включает
0-ю страницу ПЗУ, где Gluk Service. Но са─
мого  128 бейсика у меня в ПЗУ нет, вместо
него Quick Commander.

   Эмулятор  получился  такого громоздкого
размера  потому, что я хотел найти потолок
скорости. Для  реальной задачи трассировки
все  макросы работы с памятью надо было бы
загнать в подпрограммы,а многие выполнялки
команд  следовало  бы объединить. Впрочем,
потолка я не достиг.При данном методе (по─
следовательное выполнение команд без дина─
мической  рекомпиляции  и без предсказания
следующих команд) тормоз в выборе страницы
(команд,данных,стека) можно было бы сильно
уменьшить, если  заставить DE указывать не
на виртуальный адрес в памяти программы, а
на  реальный адрес (#c000-#ffff), гаранти─
ровав при этом, что в начале выбиралки ко─
манды включена именно страница команд. Для
этого  я реализовал флажок margins (0=ста─
рый  режим, 1=новый), написал  новое  ядро
эмуляции  и сделал разные макросы возврата
из выполнялок разных команд (просто выход,
выход с включением страницы команд,выход с
пересчётом  виртуального  DE  в реальный и
т.д.). В  том  виде, как я это реализовал,
пока  не  обойдён  случай, когда программа
пересекает  границу окон памяти. Но обойти
его можно. К сожалению,сам режим margins=1
даже  в этом  упрощённом виде мне так и не
удалось отладить - может быть, у вас полу─
чится?
   Есть  ещё один вариант ускорения выбора
команд  -  копировать  из страницы  команд
сразу много байт и потом выполнять команды
в нижней  памяти. Разумеется, нужно  много
проверок.

   Как обычно,я растерял в своих разорван─
ных  бумажках  записи  замеров времени для
первого метода (margins=0, который работа─
ет)  и для  второго (margins=1, который не
работает). Выигрыш  по  скорости  в  новом
методе (при  выполнении процедуры сброса),
если  мне  не изменяет память, где-то 25%.
Конечно, самый быстрый режим - это 16k.

   Эмуляции  #3d13 оказалось недостаточно,
чтобы заработали дисковые программы. Види─
мо, нужно  поддержать  ещё какие-то точки.
Mr Gluk Reset Service видит каталог, но не 
может запускать бейсики.Впрочем,дискдоктор
работает.

   Я не сделал ни одной управляющей кнопки
эмулятора, просто  отметил  в  обработчике
прерываний то место,где должны проверяться
кнопки.Рекомендую задействовать комбинацию
SS/Enter, выдавать  по  ней  меню, в числе 
опций  которого (сбросы, загрузка/выгрузка
снапшотов, отладчик  и т.д.) должна быть и
такая: "передать эмулируемой программе на─
жатие SS/Enter в течение 10 прерываний". 
 

   Исходники  лежат в приложении. Они рас─
считаны  на  компиляцию на Pentagon 512/1M 
(сам  эмулятор тоже рассчитан на работу на 
Pentagon 512/1M ). Вам  будет нетрудно ис─ 
править под другую модель памяти. 

Alone C




Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Спор - Широта ума или рабство иллюзий?
Слабо пройти? - Seaquest.
Новелла - Новелла по игре "Иван Царевич".

В этот день...   22 сентября