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