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

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

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

Я продолжаю публикацию по своему проекту портирования кода 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

.

Источник: https://habr.com/ru/post/545392/


Интересные статьи

Интересные статьи

В сентябре этого года китайская компания Huawei представила альтернативу ОС Android, систему HarmonyOS 2.0. Эта ОС предназначена для установки на планшеты, часы и смартфоны компании, по...
Предлагаем продолжить добрую традицию Ask me anything на Хабре и поговорить про разработку Android-приложений. Сегодня и завтра Android-команда Badoo будет на связи и ответит на любые...
Ваш сайт работает на 1С-Битрикс? Каждому клиенту вы даёте собственную скидку или назначаете персональную цену на товар? Со временем в вашей 1С сложилась непростая логика ценообразования и формирования...
Это уже третья попытка подружить умный дом с android, напомню, что первая попытка контролировать android устройства посредством HTTP, была с помощью приложения Paw Server. Данное приложение п...
Тема статьи навеяна результатами наблюдений за методикой создания шаблонов различными разработчиками, чьи проекты попадали мне на поддержку. Порой разобраться в, казалось бы, такой простой сущности ка...