Я продолжаю рассказывать о самодельном 8-разрядном компьютере КРИСС СР/ М. Рассказ об эмуляции процессора Z80 МК ATmega1284P. Для тех кто не читал привожу ссылки на первую, вторую и третью части
Эмулятор микропроцессора Z80 исполняет инструкции в объёме официально документированных команд с небольшими дополнениями, а кроме того, содержит ряд дополнительных команд, которые будут описаны далее. Полный список поддерживаемых команд в текущей версии приведён в таблицах 1-5. Отметим, что задача точного воспроизведения поведения процессора Z80 не ставилась, ставилась задача обеспечения работоспособности стандартных программ для ОС СР/М, что не одно и то же.
Типичный цикл выполнения однобайтовой команды состоит из следующих блоков:
— чтение кода команды из памяти — восемь тактов (см. раздел про ОЗУ);
— декодирование адреса обработчика — шесть тактов;
— переход по адресу обработчика — два такта;
— выполнение команды — один-восемь тактов, в зависимости от команды;
— назначение флагов — три—пять тактов, в зависимости от команды;
— переход к началу — два такта.
Таким образом, общая продолжительность цикла обработки для операций "запись регистр—регистр" составляет 19 тактов, арифметических и логических — 21—25 тактов, на частоте 20 МГц это соответствует времени 1 — 1,1 мкс, которое эквивалентно скорости работы процессора Z80 на частоте 4 МГц, что является для него стандартной частотой. А многие компьютеры даже работали на частоте 2,8 МГц. Поэтому компьютер обеспечивает быстродействие вполне достаточное для комфортной работы для соответствующего программного обеспечения. Плюс к этому, эмулятор не тормозится внешними запросами DMA, что имеет место в реальных системах на Z80. При разработке эмулятора задача точного соблюдения тайминга по командам не ставилась.
Из специфических особенностей эмулятора необходимо отметить следующее:
— регистр R полноценно не реализован, вместо этого работает таймер на частоте 2,5 МГц. Правило сохранения старшего бита соблюдено. Учитывая, что в современных системах задача регенерации ОЗУ не актуальна, а регистр R используется преимущественно для генерации случайных чисел, это ограничение не выглядит критичным. Автору известна только одна применявшаяся на практике конструкция LD R.A/LD A,R, которая увеличивает регистр А на 2, сохраняя значение 7-го бита, при реальной потребности легко реализовать через свободные команды;
— недокументированные флаги 3 и 5 регистра флагов не поддерживаются, они всегда равны 0. Сделано это исключительно, чтобы не тратить время на их формирование. Для работы абсолютного большинства программ это не является критичным, но некоторые тесты работают некорректно, выдавая ошибку из-за несоответствия значения регистра флагов;
— блочные команды LDIR, CPIR и тому подобные выполняются как одна команда, а не уменьшением на два счётчика команд. Это ускоряет их работу, но при входящем прерывании последнее отработается только после полного завершения цикла обработки команды;
— команда NONI отрабатывается как NOP;
— команда DAA реализована "опытным" путём: на эмуляторе проверена работа во всех комбинациях флагов Н, В, С и значений регистра А. Поэтому для части входящих значений результат может не соответствовать конкретным реализациям процессоров, но это лишь теоретически, таких проблем не выявлено.
Эмулятор корректно отрабатывает все тесты Z80 Instruction Exerciser в объёме zexdoc и стандартный тест проверки "левых" процессоров CPUTEST, большинство программ работают корректно. Но при этом есть незначительное число программ, которые демонстрируют неадекватную работу, причины выяснить пока не удалось. Автору известны два таких примера, в остальных случаях (включая все языки программирования) работа программ абсолютно корректна. Возможно, это связано с использованием "недокументированных" команд или значений флагов.
Базовый набор команд несколько расширен (все изменения в блоке команд с префиксом ED):
— блок команд операций умножения и деления;
— блок команд вызова функций BIOS пп (см. раздел про структуру ОС);
— команда вызова функций BDOS (см. раздел про структуру ОС).
Эти команды заменяют команды NONI в тех местах, где нет общепринятых "недокументированных" команд, и не влияют на работу программ, рассчитанных на обычный процессор. Правила работы с этими командами будут описаны в разделе по программированию устройства.
Говоря о структуре эмулятора, надо сказать несколько слов о структуре его организации в части вопроса о расширении возможностей и созданию эмуляторов других процессоров. Схема трёхуровневая:
— описание стандартных типов команд и операндов и список команд в формализованном виде;
— настройки кода исполнения и правила установки флагов для каждого типа команд;
— программы для формирования исполнительного кода.
Так, например, команда логической операции "И" AND В выглядит так:
— в списке команд: cmd[0xa0] = { _and_,_a_,_b_};
— в описателях команд: d[_and_] ={"and @1,@2", _____Н____, _________,
_SZ______, _Р_};
В результате автоматически формируется фрагмент исполняемого кода, выполняющий основную логику команды и выполняющий настройку флагов как стандартных, так и специфичных для конкретного контроллера.
Для описанной выше комбинации это:
z CMD_aA0: ; AND А В
and zzA.zzB
in zzTmp, SREG
;mask results flags
andi zzTmp, (_S_|_Z_|_|_|__|____)
Idi zzF, (_|_|_H_|__|__|___)
or zzF, zzTmp ;
combine flags
;set P flag
SET_P_FLAG zzA ;(___|__|___|_PJ_|___)
jmp zCMD_start
Такая комбинация многоуровневых настроек позволяет легко расширять набор команд и делать эмуляцию других типов процессоров.