[Игры и игровые приставки] Эмулятор PS2 на Android — вторая серия
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Привет всем читателям!Я продолжаю публикацию по своему проекту портирования кода PCSX2 эмулятора PS2 на Android платформу.Поспешу предупредить, что скачать и запустить не получиться — проект только на начальной стадии развития. Однако, для тех читателей, кто не лишён профессионального любопытства — добро пожаловать под кат. Что найдёт любопытствующий:
- компилируемый код для AARM64 - да, ядро PCSX2 эмулятора компилируется в нативный ARM код;
- исполняемое приложение для загрузки файлов БИСОа и образа игровых дисков;
- шок контент.
Что же, прогресс портирования зашёл достаточно далеко и получилось скомпилировать исполняемый нативный С++ код на 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.
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка под Android] Получаем результат правильно (Часть 1). Activity Result API
- [Open source, Разработка под Android, Kotlin] Reaction — обработка результатов методов в Kotlin
- [Разработка под iOS, Разработка под Android, Управление продажами, Презентации] Онбординг. Зачем нужен и как использовать
- [Фототехника, Игры и игровые приставки] Программист запустил сервер Minecraft на зеркалке Canon 200D
- [Смартфоны, Презентации, Игры и игровые приставки] Nubia представила смартфон Red Magic 6 с 18 Гб памяти
- [Монетизация игр, Игры и игровые приставки] Valve остановила работу над Artifact и перевела игру на условно-бесплатную модель
- [Игры и игровые приставки] Blizzard ещё раз подтвердила, что в ремастере Diablo II будет импорт сохранений из оригинала 20-летней давности
- [Java, Разработка мобильных приложений, Разработка под Android, Kotlin] Android — ViewPager2 — заменяем фрагменты на лету (программно)
- [Высокая производительность, Разработка мобильных приложений, IT-инфраструктура, Разработка под Android] Как мы ускорили запуск приложения Dropbox для Android на 30 % (перевод)
- [Научно-популярное, Игры и игровые приставки] Sony позволит владельцам PlayStation использовать бананы вместо джойстиков
Теги для поиска: #_igry_i_igrovye_pristavki (Игры и игровые приставки), #_ps2, #_android, #_igry_i_igrovye_pristavki (
Игры и игровые приставки
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:48
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Привет всем читателям!Я продолжаю публикацию по своему проекту портирования кода PCSX2 эмулятора PS2 на Android платформу.Поспешу предупредить, что скачать и запустить не получиться — проект только на начальной стадии развития. Однако, для тех читателей, кто не лишён профессионального любопытства — добро пожаловать под кат. Что найдёт любопытствующий:
Идея следующая - первоначально требуется выбрать файл БИОСа и файл образа диска для начала отладки кода. Полные пути к выбранным файлам сохраняются как параметры программы: 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(); } @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(); } 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; } 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; } И тут меня "ударило"!!! Область памяти, указанная как исполняемая, включает в себя буквально несколько байт кода, но сгенерированная эмуляция команд R3000A сохраняется в обычной области данных и исполняется от туда! А контроль права исполнения операционной системы и процессора куда смотрит? Для любителей повозиться с кодом - проект для среды разработки Android Studio доступен на GitHub: AndroidStudio. =========== Источник: habr.com =========== Похожие новости:
Игры и игровые приставки ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:48
Часовой пояс: UTC + 5