Эмулятор PS2 на Android — вторая серия

    Привет всем читателям!

    Я продолжаю публикацию по своему проекту портирования кода PCSX2 эмулятора PS2 на Android платформу.

    Поспешу предупредить, что скачать и запустить не получиться — проект только на начальной стадии развития. Однако, для тех читателей, кто не лишён профессионального любопытства — добро пожаловать под кат. Что найдёт любопытствующий:

    1. компилируемый код для AARM64 - да, ядро PCSX2 эмулятора компилируется в нативный ARM код;

    2. исполняемое приложение для загрузки файлов БИСОа и образа игровых дисков;

    3. шок контент.

    Что же, прогресс портирования зашёл достаточно далеко и получилось скомпилировать исполняемый нативный С++ код на AARM64. Средой разработки является Android Studio и при портировании кода с x86 на AARM64 я столкнулся с очевидной проблемой - различный набор инструкций процессоров. Многие удивятся - что за чушь, пиши на С++ и компилятор сам всё сделает. И здесь заключается сама суть проблемы портирования, с которой я столкнулся: PCSX2 создаёт исполняемый двоичный код процессора "на лету". Да, в коде эмулятора есть класс x86Emitter для записи в массив байтов байтовый код x86 процессора.

    Так что же получилось?

    Java frontend код для загрузки БИОСа и файлового образа игр. Пользовательский интерфейс прост и включает следующие окна:

    Идея следующая - первоначально требуется выбрать файл БИОСа и файл образа диска для начала отладки кода. Полные пути к выбранным файлам сохраняются как параметры программы:

        public void save()
        {
            SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(GlobalApplication.getAppContext());
    
            SharedPreferences.Editor editor = preferences.edit();
    
            try {
    
                String l_value = serialize();
    
                editor.putString(BIOS_INFO_COLLECTION, l_value);
            } catch (ParserConfigurationException e) {
                e.printStackTrace();
            } catch (TransformerException e) {
                e.printStackTrace();
            }
    
            editor.commit();
        }

    И после первоначального выбора, следующий запуск приложения будет автоматически запускать ядро эмулятора с ранее заданными БИОСом и образом диска.

        private void autoLaunch()
        {
            BIOSAdapter.getInstance();
    
            ISOAdapter.getInstance();
    
            if(PCSX2Controller.getInstance().getBiosInfo() != null &&
               PCSX2Controller.getInstance().getIsoInfo() != null)
                GameController.getInstance().PlayPause();
        }

    Портирование компилятора кода эмулятора с x86 на AARM64 представляет серьёзную проблему. Архитектура Интелл относиться к CISC с переменной длинной кода и смешанной последовательностью данных и кода, в то время как AARM64 относиться RISC с фиксированной длинной кода в 32 бита. Но проблема в том, что на синтаксисе Интелловской архитектуре завязаны десятки и десятки файлов и сотни тысяч строк кода. Не говоря о том, что возникнет проблема в совместимости кода с оригинальным PCSX2 эмулятором. Что же, решение очевидное - написать оболочку x86 синтаксиса в исполнении AARM64 кода. Да, в моём проекте нет ничего оригинального - просто попытка эмуляции х86 кода через AARM64 код.

    Конечно, я не ставлю целью закрыть всё множество х86 кодов - я поставил целью закрыть коды только используемых PCSX2 эмулятором. С этой целью в добавлен вызов нативного кода в момент создания приложения- PCSX2LibNative.getInstance().CPU_test()

           @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            checkPermission();
    
            setContentView(R.layout.activity_main);
    
    
            Button l_controlBtn = findViewById(R.id.controlBtn);
    
            if(l_controlBtn != null)
            {
               l_controlBtn.setOnClickListener(
                           new View.OnClickListener(){
                                                          @Override
                                                          public void onClick(View v) {
                                                              showControl();
                                                          }
                                                      }
    
               );
            }
    
    
            PCSX2LibNative.getInstance().CPU_test();
    
            autoLaunch();
    
        }
    

    Что делает данная функция? Генерирует AARM64 исполняемый бинарный код для множества x86 кодов и проверяет результат исполнения с значением, известным из спецификации Интелл. Пример теста AARM64 кода для SUB комманды x86:

        void execute()
        {
    
            // Установка права доступа чтение/запись для области памяти
            HostSys::MemProtectStatic(eeRecDispatchers, PageAccess_ReadWrite());
    
            // Очистка области памяти
            memset(eeRecDispatchers, 0xcc, __pagesize);
    				
            // Установка аргумента теста команды
            s_stateTest = 0x10;
    
            // Передача указателя на начало области памяти в генератор AARM64 кода
            xSetPtr(eeRecDispatchers);
    
            // Создание исполняемого кода в области памяти данных
            auto DynGen_CodeSUB = _DynGen_CodeSUB();
    
            // Установка права доступа в статус исполняемой области памяти
            HostSys::MemProtectStatic(eeRecDispatchers, PageAccess_ExecOnly());
    
            // Исполнение только что созданного AARM64 кода
            auto l_result = CallPtr((void *)DynGen_CodeSUB);
    
            // Проверка с ожидаемым результатом исполнения кода для x86!!!
            if(l_result != 2147483664)
            {
                throw L"Unimplemented!!!";
            }
        }
    
        DynGenFunc *_DynGen_CodeSUB()
        {
            // Указатель на начало исполняемой области памяти
            u8 *retval = xGetAlignedCallTarget();
    
            { // Properly scope the frame prologue/epilogue
    #ifdef ENABLE_VTUNE
                xScopedStackFrame frame(true);
    #else
                xScopedStackFrame frame(IsDevBuild);
    #endif
    
                // Загрузка в регистр eax значения аргумента теста s_stateTest 
                // по эффективному адресу &s_stateTest 
                xMOV( eax, ptr[&s_stateTest] );
    			
                // Исполнение команды x86 SUB с аргументом из регистра eax
                // и прямо заданного аргумента 0x80000000
                xSUB( eax, 0x80000000 );
            }
    
            // Выход из сгенерированной функции в вызывающую нативную функцию
            xRET();
    
            return (DynGenFunc *)retval;
        }

    Да, таких тестов исполнения эмуляции х86 кода в проекте множество - это и есть процесс разработки: исследование х86 команды и написание теста эмуляции на AARM64.

    Шок контент!!!

    При исследовании работы PCSX2 эмулятора я обратил внимание код компиляции исполнения команд процессор PS2 - R3000A:

    static DynGenFunc* _DynGen_DispatcherReg()
    {
    	u8* retval = xGetPtr();
    	
      // Загрузка значения счётчика команд в регистр eax.
    	xMOV( eax, ptr[&psxRegs.pc] );
      
      // Копирование значения счётчика команд из регистра eax в регистр ebx.
    	xMOV( ebx, eax );
      
      // Получение относительного адреса из значения счётчика команд в регистре eax  
    	xSHR( eax, 16 );
      
      // Получение адреса указателя массив указателей на начало блоков эмуляции команд R3000A
    	xMOV( rcx, ptrNative[xComplexAddress(rcx, psxRecLUT, rax*wordsize)] );
      
      // Переход по указателю на начало блока эмуляции команд R3000A
    	xJMP( ptrNative[rbx*(wordsize/4) + rcx] );
    	return (DynGenFunc*)retval;
    }

    Где psxRegs.pc - переменная для хранения значения счётчика команд процессора R3000A, psxRecLUT - указатель на массив указателей на скомпилированные R3000A команды. Схема работы кода имеет следующий вид:

    И тут меня "ударило"!!!

    Область памяти, указанная как исполняемая, включает в себя буквально несколько байт кода, но сгенерированная эмуляция команд R3000A сохраняется в обычной области данных и исполняется от туда! А контроль права исполнения операционной системы и процессора куда смотрит?

    Для любителей повозиться с кодом - проект для среды разработки Android Studio доступен на GitHub: AndroidStudio

    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 2

      +1

      Это все прекрасно но имхо будущее всё-таки за рекомпиляцией самой игры под целевую платформу а не эмуляцией.
      Зы ни один из существующих эмуляторов ps2 не показал достаточного быстродействия на моем телефоне honor 10i несмотря на далеко не самый слабый процессор.
      Хотя и на компе эмулятор pcsx2 не особо радует интересная мне игра работает вкривь и вкось хотя уж тут то производительности с большим запасом

        +1
        Авторское право, потерянные исходники, просто гордость превыше всего как у Сеги?

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

      Самое читаемое