Важная информация

User Tag List

Страница 1 из 3 123 ПоследняяПоследняя
Показано с 1 по 10 из 28

Тема: Как сократить код эмулятора Z80 на PC

  1. #1
    Master Аватар для Vladimir Kladov
    Регистрация
    09.02.2005
    Адрес
    Новосибирск
    Сообщений
    933
    Спасибо Благодарностей отдано 
    0
    Спасибо Благодарностей получено 
    17
    Поблагодарили
    17 сообщений
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию Как сократить код эмулятора Z80 на PC

    Эта идея зреет во мне весьма давно, и кажется обстоятельства приперли, соответственно, я решил приступить к ее реализации. Обстоятельства - это немерянно выросший размер экзешника, уже 1,68М несжатый, или ~650К сжатый. На PC код растет очень быстро, особенно если его делать на ЯВУ. Я могу переписать весь интерфейс на асм, но опыт показывает, что код сократится не на очень много, а вот мороки с его сопровождением тогда будет выше крыши. Как раз потому, что PC-Asm, это вам не тут. Поэтому на PC-Asm я делаю только эмуляцию ядра, и видео-рендеринг. Весь интерфейс на данный момент сделан в Delphi+KOL.

    В чем суть идеи (разрешаю пользоваться, не жадный я по части идей). Часть кода переписывается на асме Z2x80 (сейчас объясню, чем оно отличается от чистого Z80), и выполняется под управлением той части эмулятора, что написана на асме, и умеет выполнять код Z80. Замечу, что у меня используется свой макро-ассемблер, который компилирует код на лету, поэтому для всех возможных вариантов эмуляции, в том числе Gfx256, с мультиколором, без него, и прочими вариантами (а теперь еще один добавится) - код используется для эмуляции общий. Так было не всегда, в первых версиях для каждого варианта был свой собственный код, зато он был сразу скомпилирован, и быстрее запрягал (теперь запрягает дольше чуток, зато едет еще быстрее чем раньше).

    Теперь о Z2x80. Это не реальный проц из жизни. Это просто тот же Z80, но с удвоенной шиной данных. Т.е. 1-байтовые регистры A, B, C, D, E, H, L становятся 2х-байтными, 2х-байтовые регистровые пары соответственно 4х-байтовыми. Естественно, 2 набора регистров, основной и альтернативный. Внешне команды не меняются, коды операции у них не меняются, иногда увеличивается (удваивается в основном) разрядность непосредственного операнда. Иногда нужно переключаться как бы в "обычный" 1-байтный режим, на 1 команду или на несколько. Для этого можно использовать 1-байтовые префиксы, эквивалентные "ненужным" nop-эквивалентным командам. Например LD B,B - как префикс, превращающий следующую команду в 1-байтовую. Адресное пространство для этой "машины" - совпадает с адресным пространством самого эмулятора. Стек - со стеком машины PC. Чтобы не создавать проблем с абсолютной адресацией, команды JP и CALL разрядность не меняют, т.е. занимают те же 3 байта, но переходы становятся относительными. С командами загрузки / записи по непосредственному адресу я пока не решил, что делать. Вроде бы относительная адресация была бы полезна, чтобы так же делать самомодифицирующийся код или брать данные из кода. Но и обращение к глобальным переменным может потребоваться, а о том, как передать адрес этих переменных в Z-код, я еще только подумать успел. Самый простой вариант - загружать в EBP на входе в эмулятор (при вызове функции, переписанной на Z2x80) адрес некоторой общей структуры, через которую происходит обмен данными между кодом PC и кодом Z2x80. И использовать далее EBP как базу при обращениях с абсолютной адресацией. Можно сделать относительную адресацию к коду основной, а обращение к общим данным - через префикс, например LD E,E. Есть еще ряд команд, которые вообще не требуются при эмуляции Z2x80. Например, IN A,(d), OUT (d),A. Их можно задействовать как короткие вызовы до 256 часто используемых функций по таблицам. Например, IN - для вызова таких же функций, уже переделанных с PC на Z, OUT - для вызова машинного кода PC.

    Хочется привести пример, и наконец закончить на этом. Допустим есть у меня такой код:

    procedure TFormConfig.SetupColors;
    var I: Integer;
    begin
    for I := 0 to 15 do
    ColorPanels[ I ].Color := Color16ToColor( Colors16[ I ] );
    end;

    Как бы его можно было переписать на Z2x80:

    TFormConfig_SetupColors PROC
    DEFB StartZ2x80
    E=E : HL=adr_Colors16 : E=E: IX=adr_ColorPanels : B=16
    LOOP
    E=(HL) : DE><HL
    CALL Color16ToColor
    PUSH HL : HL=(IX) : PUSH HL
    C=C : CALL TControl_SetColor
    DE><HL
    ELOOPB
    RET

    Пояснения. StartZ2x80 - это байт $E7, вопринимается PC как недопустимая команда, после чего срабатывает мой SEH-обработчик, и передает управление эмулятору Z2x80, с тем чтобы он начал эмулировать код со следующего байта. E=E : HL=adr_Colors16 загружает поле "общей структуры", хранящее адрес начала массива Colors16. Аналогично, в IX далее грузится адрес массива 16 панелей (типа TControl). Далее в 16-битный регистр E загружается слово, являющееся цветом в формате pf16bit, из очередного элемента массива Colors16. Функция Z2x80 конвертирует его в формат pf32bit (получает параметр в L, возвращает результат в HL, например). Кто не знаком с нотациями C--, >< это обмен (т.е. DE><HL ~ EX DE,HL). С нотацией = проблем быть не должно. LOOP ... ELOOPB это цикл DJNZ с авто-меткой (LOOP - это как бы метка). RET выполнит переход в точку, которую подготовил эмулятор при входе в процедуру из PC-кода (и тогда вернет на место регистры, задействованные для эмуляции, и вернутся в PС-код), либо если вход был выполнен непосредственно из Z2x80-кода, будет продолжатся эмуляция вызвавшей Z-процедуры.

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

    Теперь можно посчитать прибыль (в смысле убыль, кода). На PC-Asm тот же код занимает ... щас гляну ... 56 байтов. На Z2x80 - 30 байтов. Хм, я уже занимался подобными "переводами" на асм на PC (см. KOL), и могу заметить, что чем больше переводимая процедура, тем лучше результаты. Но даже на такой мелочи почти вдвое выиграть, я не очень ожидал такого.

    Собственно, частично идея мной уже опробована (SEH-обработчик например), но я пробовал другое - форт-машину. Эмулятор для нее занял в коде PC всего 300 байт. И код мог бы быть весьма краток, если бы... я умел писать на форте. Как показала реальность, "думать" в терминах стековой машины мне неудобно. Отладка занимает бОльшую часть программирования, а я этого не люблю. Z80 все-таки мне более привычен, как бывшему спектрумисту, и хотя его эмулятор занимает до 100К, он ведь все равно уже есть в коде, то грех им не воспользоваться как интерпретатором байт-кода. Получается эмулятор на эмуляторе, или сам себя эмулирует, вот так.

  2. #1
    С любовью к вам, Yandex.Direct
    Размещение рекламы на форуме способствует его дальнейшему развитию

  3. #2
    Veteran
    Регистрация
    04.08.2005
    Адрес
    Nizhnevartovsk
    Сообщений
    1,018
    Спасибо Благодарностей отдано 
    86
    Спасибо Благодарностей получено 
    132
    Поблагодарили
    85 сообщений
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от Vladimir Kladov
    Получается эмулятор на эмуляторе, или сам себя эмулирует, вот так.
    "Роботы делают роботов - какое извращение..."(с)c3po
    По-моему это изврат. ПРоблема найдена на ровном месте.
    Может быть стОит перейти на CPP? Unreal вот например, будучи запакованным UPX'ом весит всего 170 кил.

  4. #3
    Veteran Аватар для SMT
    Регистрация
    16.01.2005
    Адрес
    Бобруйск
    Сообщений
    1,267
    Спасибо Благодарностей отдано 
    0
    Спасибо Благодарностей получено 
    0
    Поблагодарили
    0 сообщений
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    половинное решение. я так понимаю, pc-asm не устраивает низкой плотностью кода, но если не торопиться и не жалеть времени на реализацию, выгоднее брать за основу не z80-asm (почему именно z80?), а псевдо-код, максимально приспособленный под задачу, ну или максимально удобный (много регистров, свои режимы адресации и т.п.)

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

    ну и самый рулез - сделать оптимизирующий компилятор с си (если удобнее - с паскаля), потому что переписывать код с каждой новой ревизией скрипта - мартышкин труд

  5. #4
    Veteran Аватар для SMT
    Регистрация
    16.01.2005
    Адрес
    Бобруйск
    Сообщений
    1,267
    Спасибо Благодарностей отдано 
    0
    Спасибо Благодарностей получено 
    0
    Поблагодарили
    0 сообщений
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    форт идеален в плане сокращения объема тем, что нет избыточной информации, в каких регистрах/ячейках хранятся промежуточные результаты. в записи арифметического или логического выражения лишь операнды и операторы, и то все в кучу

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

    ну и дожимать - хранить не адрес слова, а его номер. причём юзать код переменной длины (1 байт - часто используемые слова, 2 - остальные). в идеале Хаффманом/арифметиком жать, если скорость не важна

  6. #5
    Master Аватар для Vladimir Kladov
    Регистрация
    09.02.2005
    Адрес
    Новосибирск
    Сообщений
    933
    Спасибо Благодарностей отдано 
    0
    Спасибо Благодарностей получено 
    17
    Поблагодарили
    17 сообщений
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    да я это все сделал, замучиться только код на этом форте писать. Надо же по-другому, по-стековому думать. Все время помнить, сколько в стеке чего и в каком порядке лежит.

    А насчет С++ - не надо. У меня GUI интерфейс тянет, а не код эмулятора. Просто полазьте по меню, пооткрывайте окна. Их там - море и маленький вагон. На С++ это еще тяжелее будет весить. И переписывать все это - спасибки. Мне на паскале удобнее, не говоря уже о скорости компиляции. (Люди говоривали, что Delphi частично подготавливает код к компиляции прямо во время редактирования текста. А и правильно, чего процессор простаивает, пока мы тут клаву давим, пусть работает).

  7. #6
    Veteran
    Регистрация
    04.08.2005
    Адрес
    Nizhnevartovsk
    Сообщений
    1,018
    Спасибо Благодарностей отдано 
    86
    Спасибо Благодарностей получено 
    132
    Поблагодарили
    85 сообщений
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от Vladimir Kladov
    Люди говоривали, что Delphi частично подготавливает код к компиляции прямо во время редактирования текста. А и правильно, чего процессор простаивает, пока мы тут клаву давим, пусть работает
    Ничего он не подготавливает. Байки это.

  8. #7
    Veteran Аватар для SMT
    Регистрация
    16.01.2005
    Адрес
    Бобруйск
    Сообщений
    1,267
    Спасибо Благодарностей отдано 
    0
    Спасибо Благодарностей получено 
    0
    Поблагодарили
    0 сообщений
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

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

  9. #8
    Veteran Аватар для SMT
    Регистрация
    16.01.2005
    Адрес
    Бобруйск
    Сообщений
    1,267
    Спасибо Благодарностей отдано 
    0
    Спасибо Благодарностей получено 
    0
    Поблагодарили
    0 сообщений
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

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

    Код:
    #ifdef FUNC1_REAL
    void func1(int a) { ... }
    #else
    #include "func1_pcode.cpp"
    #end
    а препроцессор читает func1 из основного исходника и делает файл func1_pcode.cpp вида
    Код:
    static const func1_code[] = { 0xC3, 0x00, 0x00, ... };
    #define func1(a) forth_call(func1_code,a)

  10. #9
    Master Аватар для Vladimir Kladov
    Регистрация
    09.02.2005
    Адрес
    Новосибирск
    Сообщений
    933
    Спасибо Благодарностей отдано 
    0
    Спасибо Благодарностей получено 
    17
    Поблагодарили
    17 сообщений
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

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

  11. #10
    Member Аватар для thims
    Регистрация
    18.01.2005
    Адрес
    Калининград, Россия
    Сообщений
    110
    Спасибо Благодарностей отдано 
    7
    Спасибо Благодарностей получено 
    3
    Поблагодарили
    1 сообщение
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Ну это-то проверить легко. Запускаем filemon, ставим фильтр на delphi, топчем исходник и смотрим... Вряд ли он в память компилит (если компилит, я очень сомневаюсь).

Страница 1 из 3 123 ПоследняяПоследняя

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •