Сообщения без ответов | Активные темы Текущее время: 27 апр 2024, 16:24



Ответить на тему  [ Сообщений: 7 ] 
 [PSX] Хачим Pops 
Автор Сообщение
Сообщение 04 ноя 2010, 19:09
Профиль

Зарегистрирован:
04 ноя 2010, 18:55
Сообщения: 29
Привет это org :)

Pops - это официальный эмулятор PlayStation (PSX) для Sony PSP. Не зря я потратил прилично времени на знакомство с устройством PSP, попробуем как-нибудь применить эти знания.

Pops представляет собой 2 модуля: pops.prx и popsman.prx (менеджер). В pops.prx находится сам эмулятор, менеджер - это обвеска.

Зачем ломать? Интересно выяснить работу некоторых недокументированных регистров PSX при эмуляции BIOS, интересно посмотреть как эмулируется GTE и пр.
Как будем ломать: Pops 100% низкоуровневый эмулятор, который ничем не отличается от остальных подобных эмуляторов, то есть берём инструкцию, интерпретируем её (скорее всего), всё железо эмулируется средствами PSP. В состав pops.prx входит кастомный BIOS для PSX.

Пока не понятно есть ли что-то ценное в popsman.prx, так что его пока не трогаем. MD5 хеш моей версии pops.prx: 151bf8250ea0eb61988e00fbd452b463

Первым делом вырежем BIOS и попробуем его сэмулировать на обычных эмуляторах.

Кастомный Pops BIOS.
Первым делом нужно найти сигнатуру - строку "Sony Computer Entertainment CEX-3000... by K.S." или как то так. Находится эта строка в районе 0x06BC30. Выше как известно находится стартовый код загрузки BIOS. Находим это место и вырезаем 512 Кб (или сколько получится, потому что BIOS кастомный и возможно кастрированный). Получаем начало BIOS из дизассемблированного pops.prx, полученного с помощью утилиты prxtool:

0x0006BA8C: 0x3C080013 '...<' - lui $t0, 0x13
0x0006BA90: 0x3508243F '?$.5' - ori $t0, $t0, 0x243F
0x0006BA94: 0x3C011F80 '...<' - lui $at, 0x1F80
0x0006BA98: 0xAC281010 '..(.' - sw $t0, 4112($at)
0x0006BA9C: 0x00000000 '....' - nop
0x0006BAA0: 0x24080B88 '...$' - li $t0, 2952
0x0006BAA4: 0x3C011F80 '...<' - lui $at, 0x1F80
0x0006BAA8: 0xAC281060 '`.(.' - sw $t0, 4192($at)
0x0006BAAC: 0x00000000 '....' - nop

Начало типичного BIOS :) Так как коды PSP CPU и R3000 совпадают, prxtool выдал сразу читаемый текст BIOS.
Это место соответствует смещению в файле: 0x06BB2C.
Версия BIOS походу дела соответствует SCPH-1001 (наиболее популярная версия, используемая во фриварных эмуляторах), так как дата BIOS, записанная перед сигнатурой:
0x0006BB8C: 0x19951204 '....' - 1995.12.04
0x0006BB90: 0x00000003 '....' -

Запустить сграбленный BIOS мне удалось на ePSXe:

Изображение

psX вылетает с ошибкой, на остальных эмуляторах запускать не пробовал.

Как я и думал из BIOS вырезали белую заставку (её время занимает заставка загрузки приложения PSP) и SHELL (менеджер карт памяти и CD-проигрыватель). Оставили только минимальную часть - загрузчик ядра PSX Kernel и CD-лоадер.

Скачать Pops BIOS: http://psxdev.narod.ru/download/PopsBIOS.zip


Сообщение 04 ноя 2010, 21:27
Профиль

Зарегистрирован:
04 ноя 2010, 18:55
Сообщения: 29
Внутри Pops.

Теперь нам как-то нужно собственно похачить Pops. Последовательно реверсить pops.prx от module_start будет непосильной задачей, притом что не известны названия многих импортируемых вызовов PSP из других модулей.

Чтобы добраться до внутренностей эмулятора можно предположить что он работает как все остальные интерпретируемые эмуляторы.
То есть у него есть модуль интерпретации инструкций R3000, обработчик доступа к хардварным регистрам, модули эмуляции GPU/SPU и прочих аппаратных узлов PSX.

Соответственно нужно за что-то "зацепиться", а так как prxtool даёт в листинге перекрёстные ссылки, то у нас есть прекрасная возможность найти процедуры эмуляции вплоть до главной петли (или как обычно в модулях PSP - до главного потока модуля).

Попробуем зацепиться за обработку доступа к аппаратным регистрам PSX:
0x1F801000-0x1F80xxxx - HARDWARE I/O
Список всех документированных регистров PSX можно найти тут: http://psxdev.narod.ru/docs/memlayout.htm
Кстати в адресном пространстве PSP этот диапазон никак не используется.

Нужно найти в листинге упоминание 0x1f80, исключая область PSX BIOS (которая тоже дизассемблируется prxtool):

sub_00001030:
0x000010A0: 0x3C0F1F80 '...<' - lui $t7, 0x1F80

0x00001128: 0x3C061F80 '...<' - lui $a2, 0x1F80

0x000011B0: 0x3C031F80 '...<' - lui $v1, 0x1F80

0x000011C4: 0x3C0D1F80 '...<' - lui $t5, 0x1F80
0x000011C8: 0x3C091F80 '...<' - lui $t1, 0x1F80

0x000011D8: 0x3C0B1F80 '...<' - lui $t3, 0x1F80

sub_000012BC:
0x000012BC: 0x3C031F80 '...<' - lui $v1, 0x1F80

0x000012E4: 0x3C021F80 '...<' - lui $v0, 0x1F80

0x000012FC: 0x3C021F80 '...<' - lui $v0, 0x1F80

.......

0x0000AE14: 0x3C021F80 '...<' - lui $v0, 0x1F80
0x0000AE2C: 0x34441070 'p.D4' - ori $a0, $v0, 0x1070 INTC

0x0000B434: 0x3C051F80 '...<' - lui $a1, 0x1F80
0x0000B43C: 0x34A410E0 '...4' - ori $a0, $a1, 0x10E0 DMA GPU OT (канал 6)

0x0000B478: 0x3C021F80 '...<' - lui $v0, 0x1F80
0x0000B484: 0x344410F0 '..D4' - ori $a0, $v0, 0x10F0 DMA INTC (управление DMAC)

0x0000B4B4: 0x3C071F80 '...<' - lui $a3, 0x1F80
0x0000B4BC: 0x34E31080 '...4' - ori $v1, $a3, 0x1080 DMA MDEC (канал 0)

0x0000BF0C: 0x3C021F80 '...<' - lui $v0, 0x1F80
0x0000BF24: 0x34441100 '..D4' - ori $a0, $v0, 0x1100 ROOT COUNTERS

0x0000C3B0: 0x3C021F80 '...<' - lui $v0, 0x1F80
0x0000C3C8: 0x34441810 '..D4' - ori $a0, $v0, 0x1810 GPU

0x0000C60C: 0x3C021F80 '...<' - lui $v0, 0x1F80
0x0000C624: 0x34441040 '@.D4' - ori $a0, $v0, 0x1040 SIO

0x0000F018: 0x3C021F80 '...<' - lui $v0, 0x1F80
0x0000F030: 0x34441820 ' .D4' - ori $a0, $v0, 0x1820 MDEC

0x00010A1C: 0x3C021F80 '...<' - lui $v0, 0x1F80
0x00010A40: 0x34441800 '..D4' - ori $a0, $v0, 0x1800 CDROM

0x00026C38: 0x3C051F80 '...<' - lui $a1, 0x1F80
0x00026C58: 0x3C021F80 '...<' - lui $v0, 0x1F80
0x00026C70: 0x34441C00 '..D4' - ori $a0, $v0, 0x1C00 SPU

Как видно таких областей совсем немного, причём если вначале по контексту некоторые куски кода напоминают выбор условия (видимо там и находится обработчик доступа к регистрам), то последующие вхождения напоминают непосредственно обработчики аппаратных узлов. Дополнительно я пометил обозначения устройств PSX, на которые ссылаются куски кода по контексту.

Дизассемблированный листинг pops.prx без PSX BIOS (6.5 MB): http://psxdev.narod.ru/download/pops.txt


Сообщение 05 ноя 2010, 02:09
Профиль

Зарегистрирован:
04 ноя 2010, 18:55
Сообщения: 29
Работа с памятью 1.

Напомню карту физической памяти PSX:
•0x00000000-0x001FFFFF - RAM
•0x1F000000-0x1F7FFFFF - память устройства, подсоединенного к параллельному порту (PIO)
•0x1F800000-0x1F8003FF - Scratch (кэш данных)
•0x1F801000-0x1F80???? - регистры аппаратуры
•0x1FC00000-0x1FC7FFFF - ROM
•0x1FFE0130 - недокументированный регистр.

Очердь за разбором кода, где упоминается 0x1f80.

Первый кусок - это процедура для чтения памяти:

Код:
u8 rombios[512*1024] = { .... } ; // тут находится кастомный ROM BIOS Pops

// read_type: 0 - lb, 1 - lh, 2 - lw, 3 - read 0, 4 - lbu, 5 - lhu
sub_106C:
u32 ReadMemory (u32 address, int read_type)
{
   address &= 0x1fffffff;   // обрезать старшие 3 бита (трансляция адреса, у PSX CPU нет MMU)

   if ( (address-0x1fc00000) < 0x80000 ) {   // Прочитать область ROM BIOS
      void *ptr = rombios + address & 0x7ffff;

      switch ( read_type )
      {
         case 0: return *(s8 *)ptr;   // lb
         case 1: return *(s16 *)ptr;   // lh
         case 2: return *(s32 *)ptr;   // lw
         case 4: return *(u8 *)ptr;   // lbu
         case 5: return *(u16 *)ptr;   // lhu
      }

   }
   else {   // остальная память

      if ( address == 0x1f802030 || address == 0x1f802040 )    // отладочные регистры
      {
         if (read_type < 3) return 0xffffffff;   // lb, lh и lw
         else if (read_type == 4 ) return 0xff;   // lbu
         else return 0xffff;   // lhu
      }

      if ( address > 0x1f802040 )   // недокументированный регистр 0x1ffe0130. Его значение хранится в переменной.
      {
         if ( address == 0x1ffe0130 ) return [0x0011198C];
      }

      if ( (address - 0x1f000000) < 0x800000 ) return 0;   // PIO (устройство не подсоединено)

      GenException? ( 7, 0 );   // Скорее всего 7 это код исключения : DBE Bus error on Data load.

      if (read_type < 3) return 0xffffffff;   // lb, lh и lw
      else if (read_type == 4 ) return 0xff;   // lbu
      else return 0xffff;   // lhu
   }

}

исходник представляет собой псевдокод на Си.

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

- Трансляция адреса происходит просто: обрезанием 3х старших битов. Точно также делает и реальный PSX CPU, потому что в нём отсутствует блок трансляции адресов (MMU).
- При попытке доступа к неиспользуемым диапазонам памяти возвращается 0xff и вызывается исключение DBE (#7). Я ещё не разбирал процедуру генерации исключения, но очевидно что Pops умеет это делать (в отличии от всех известных фриварных эмуляторов).
- При попытке чтения памяти устройства PIO возвращается 0.
- Значение недокументированного регистра 0xFFFE0130 хранится в переменной, так что Pops его где-то использует ещё.
- В процедуре чтения упоминаются отладочные регистры 0x1f802030 и 0x1f802040, но никак не используются. Попытка их чтения не вызывает исключения DBE, но возвращается 0xff (как будто память не используется).
- В этой процедуре не происходит обработка чтения Scrath pad буфера и доступа к хардварным регистрам. Наверное проверка на доступ к ним производится в другом месте.
- Чтение из RAM также не обрабатывается, видимо тоже находится в другом месте.


Последний раз редактировалось organic 05 ноя 2010, 04:28, всего редактировалось 4 раз(а).



Сообщение 05 ноя 2010, 03:57
Профиль

Зарегистрирован:
04 ноя 2010, 18:55
Сообщения: 29
Работа с памятью 2.

Второй кусок более простой, но от этого не менее интересный. Тут происходит типичная инициализация "заглушек" на неиспользуемые регистры, для этого используется функция установки хука:

// Содержит в себе обработчик чтения/записи в регистр.
typedef struct HookCB {
u32 (*readCB)(u32 addr, int read_type);
void (*writeCB)(u32 addr, u32 value, int write_type);
} HookCB;

Сама таблица хуков располагается в памяти PSP по адресу 0x10000.

Код:
sub_1018:
void InstallHardwareHooks ( u32 address, int length, u32 (*readcb)(u32 addr, int read_type), void (*writecb)(u32 addr, u32 value, int write_type) )
{
   if (readcb == NULL) readcb = defaultReadHook;
   
   if (writecb == NULL) writecb = defaultWriteHook;

   int cur = address / 8;
   int amount = length / 8;

   HookCB * ptr = address - 0x1f7f0000;   // по адресам 0x10000 ... располагается таблица хуков для обработки HW регистров.
   int end = cur + amount;

   do {
      cur++;
      ptr.readCB = readcb;
      ptr.writeCB = writecb;
      ptr++;
   } while ( cur != end );
}

Пустышки (используются, если не указан обработчик чтения или записи регистра):

u32 defaultReadHook ( u32 address, int read_type ) :
0x00001320: 0x3C020000 '...<' - lui $v0, 0x0
0x00001324: 0x7CA228C4 '.(.|' - ins $v0, $a1, 3, 3
0x00001328: 0x24421338 '8.B$' - addiu $v0, $v0, 4920
0x0000132C: 0x3C010001 '...<' - lui $at, 0x1
0x00001330: 0x00400008 '..@.' - jr $v0
0x00001334: 0x7C815804 '.X.|' - ins $at, $a0, 0, 12

0x00001338: 0x03E00008 '....' - jr $ra
0x0000133C: 0x80222000 '. ".' - lb $v0, 8192($at) // lb
0x00001340: 0x03E00008 '....' - jr $ra
0x00001344: 0x84222000 '. ".' - lh $v0, 8192($at) // lh
0x00001348: 0x03E00008 '....' - jr $ra
0x0000134C: 0x8C222000 '. ".' - lw $v0, 8192($at) // lw
0x00001350: 0x03E00008 '....' - jr $ra
0x00001354: 0x24020000 '...$' - li $v0, 0 // return 0
0x00001358: 0x03E00008 '....' - jr $ra
0x0000135C: 0x90222000 '. ".' - lbu $v0, 8192($at) // lbu
0x00001360: 0x03E00008 '....' - jr $ra
0x00001364: 0x94222000 '. ".' - lhu $v0, 8192($at) // lhu
0x00001368: 0x03E00008 '....' - jr $ra
0x0000136C: 0x00001021 '!...' - move $v0, $zr // return 0

void defaultWriteHook ( u32 address, u32 value, int write_type ) :
0x00001370: 0x3C020000 '...<' - lui $v0, 0x0
0x00001374: 0x7CC220C4 '. .|' - ins $v0, $a2, 3, 2
0x00001378: 0x24421388 '..B$' - addiu $v0, $v0, 5000
0x0000137C: 0x3C010001 '...<' - lui $at, 0x1
0x00001380: 0x00400008 '..@.' - jr $v0
0x00001384: 0x7C815804 '.X.|' - ins $at, $a0, 0, 12

0x00001388: 0x03E00008 '....' - jr $ra
0x0000138C: 0xA0252000 '. %.' - sb $a1, 8192($at) // sb
0x00001390: 0x03E00008 '....' - jr $ra
0x00001394: 0xA4252000 '. %.' - sh $a1, 8192($at) // sh
0x00001398: 0x03E00008 '....' - jr $ra
0x0000139C: 0xAC252000 '. %.' - sw $a1, 8192($at) // sw
0x000013A0: 0x03E00008 '....' - jr $ra
0x000013A4: 0x00000000 '....' - nop // dummy

Ну и сама функция инициализации "пустых" обработчиков для HW регистров:

Код:
sub_12BC:
void InitHardwareHooks ()
{
   InstallHardwareHooks ( 0x1f801000, 0x1000, ReadMemory, WriteMemory );
   InstallHardwareHooks ( 0x1f801060, 16, NULL, NULL );
   InstallHardwareHooks ( 0x1f801000, 64, NULL, NULL );
}

Как видно функция вначале устанавливает стандартные обработчики ReadMemory / WriteMemory для всех регистров (при попытке доступа будет сгенерировано исключение DBE), потом устанавливает "пустышки" для чтения/записи неиспользуемых регистров PSX :
0x1f801060 - 0x1f801070
0x1f801000 - 0x1f801040
чтобы при доступе к ним не возникало исключения DBE.

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


Сообщение 05 ноя 2010, 16:23
Профиль

Зарегистрирован:
04 ноя 2010, 18:55
Сообщения: 29
Версия Pops и module_start.

Решил всё таки копнуть Pops с самого начала, оказалось кода не очень много:

Код:
module_start ()
{
   SysMemUserForUser_7591C7DB ( 0x03050210 );
   SysMemUserForUser_F77D77CB ( 0x00030306 );

   thread = sceKernelCreateThread ( "popsmain", PopsMain, 17, 0x40000, 0x80004000, 0 );
   sceKernelStartThread (thread, 0, 0);
   return 0;
}

sub_0068
void PopsMain ()
{
   static char popsversion[64] = "branches/pops-352/src(r5285)";

   strncpy ( 0x00013FC0, popsversion, 64 );

   *(u8 *)(0x13F00) = 0xff;

   cb = sceKernelCreateCallback ( "exitCB", PopsExitCallback, 0, 0 );
   sceKernelRegisterExitCallback ( cb );

   cb = sceKernelCreateCallback ( "powerCB", sub_2308C, 0, 0 );
   scePowerRegisterCallback ( "powerCB", cb );

   // Инициализация подсистем эмулятора.
   
   if ( AllocPSXMemory ( -1 ) < 0 ) scePopsMan_0090B2C8 ( 0x80000004 );

   if ( sub_000135D0 () < 0 ) scePopsMan_0090B2C8 ( 0x80000004 );

   if (sub_0001E520 ()) scePopsMan_0090B2C8 ( 0x80000004 );

   if ( sub_00012AE8 () ) scePopsMan_0090B2C8 ( 0x80000004 );

   if ( sub_00015010 () < 0 ) scePopsMan_0090B2C8 ( 0x80000004 );

   if ( sub_00014C1C () < 0 ) scePopsMan_0090B2C8 ( 0x80000004 );

   sub_00000494 ();
   sub_0002137C (0);
   sub_00000508 ();

   sceImpose_A9884B00 (0);
   sceKernelExitGame ();
}

sub_01CC:
PopsExitCallback ()
{
   sceImpose_A9884B00 (0);
   sceKernelExitGame ();
   return 0
}

sub_05A8:
int AllocPSXMemory ()
{
   return sceKernelAllocPartitionMemory ( 2, "PSXMemory", 2, 0xA00100, 0x09580000);
}

Попутно выяснил версию своего Pops: branches/pops-352/src(r5285)


Сообщение 06 ноя 2010, 03:10
Профиль

Зарегистрирован:
04 ноя 2010, 18:55
Сообщения: 29
GTE.

Не найдя способа получить обработчик инструкций LB/LH/LW я решил покопать процедуру GenException. Я думал что это процедура генерации исключения, но оказалось что это не так. Разбор приводить не буду, там не всё понятно, но совершенно случайно я натолкнулся на операцию var & 0x3f и ссылку на следующую таблицу:

(0x0002CBD8):
0x00001010 0x0000DEFC 0x00001010 0x00001010 0x00001010 0x00001010 0x0000E58C 0x00001010
0x00001010 0x00001010 0x00001010 0x00001010 0x00000FE4 0x00001010 0x00001010 0x00001010
0x0000D37C 0x0000E7FC 0x00000F00 0x0000D678 0x0000E668 0x00001010 0x0000D7B8 0x00001010
0x00001010 0x00001010 0x00001010 0x0000D9DC 0x0000D5A4 0x00001010 0x0000DCB0 0x00001010
0x0000DD94 0x00001010 0x00001010 0x00001010 0x00001010 0x00001010 0x00001010 0x00001010
0x00000F60 0x0000D4A8 0x0000E958 0x00001010 0x00001010 0x0000E5D0 0x0000E618 0x00001010
0x0000E36C 0x00001010 0x00001010 0x00001010 0x00001010 0x00001010 0x00001010 0x00001010
0x00001010 0x00001010 0x00001010 0x00001010 0x00001010 0x00000F8C 0x00000FB8 0x0000DAF0

хорошенько присмотрелся к ней и понял что мне дико повезло :lol:

void (*psxCP2[64])() = {
psxBASIC, gteRTPS , psxNULL , psxNULL, psxNULL, psxNULL , gteNCLIP, psxNULL, // 00
psxNULL , psxNULL , psxNULL , psxNULL, gteOP , psxNULL , psxNULL , psxNULL, // 08
gteDPCS , gteINTPL, gteMVMVA, gteNCDS, gteCDP , psxNULL , gteNCDT , psxNULL, // 10
psxNULL , psxNULL , psxNULL , gteNCCS, gteCC , psxNULL , gteNCS , psxNULL, // 18
gteNCT , psxNULL , psxNULL , psxNULL, psxNULL, psxNULL , psxNULL , psxNULL, // 20
gteSQR , gteDCPL , gteDPCT , psxNULL, psxNULL, gteAVSZ3, gteAVSZ4, psxNULL, // 28
gteRTPT , psxNULL , psxNULL , psxNULL, psxNULL, psxNULL , psxNULL , psxNULL, // 30
psxNULL , psxNULL , psxNULL , psxNULL, psxNULL, gteGPF , gteGPL , gteNCCT // 38
};

Это таблица опкодов GTE.

Все обработчики команд я повырезал из листинга pops.prx и положил в архив: http://psxdev.narod.ru/download/PopsGTE.zip (4.5 MB)
Дополнительно там находится сверхсекретный мануал по инструкциям PSP VFPU. VFPU очень активно используется Pops для эмуляции GTE. Ещё несколько замечаний находятся в файле PopsGTE.txt

Полный разбор всех команд GTE чрезвычайно трудоёмкая задача, потому что там используется ручной оптимизированный код VFPU. Однако я уже разобрал парочку команд и положил результаты в файл PopsGte.c

Конечной целью этой ветви исследования является замена модуля Gte.c из эмулятора Pcsx и проверка как Pops GTE эмулирует игры. Короче говоря попробуем произвести пересадку органов из Pops в Pcsx и последим за пациентом :crazy:


Сообщение 20 июл 2011, 16:27
Профиль

Зарегистрирован:
04 ноя 2010, 18:55
Сообщения: 29
На гугле создал проект : http://code.google.com/p/pops-gte/


Показать сообщения за:  Поле сортировки  
Ответить на тему   [ Сообщений: 7 ] 

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 52


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

Найти:
Перейти:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by STSoftware for PTF (mod by Zeru-j).
Русская поддержка phpBB