BIOS
512 KB ROM содержит стартовый загрузчик BIOS, копию ядра (kernel) PlayStation OS, а также "оболочку" (shell), которая открывается, если в консоль не вставлен игровой диск и содержит менеджер карт памяти и CD-проигрыватель.
Типичный ROM BIOS выглядит примерно вот так :
- У BIOS PU-7 и старых PU-8 микросхема 40 выводов.
- Начиная с новых версий PU-8 (и далее) микросхема 32 вывода (в том числе и у PSOne)
Внутри скорее всего ROM с ионной имплантацией по маске.
Contents |
Тайминги
Версии BIOS
Вот это сложный вопрос, потому что версии BIOS во-первых отличаются от региона, во вторых они отличаются между моделями материнских плат. И даже внутри одной модели материнки могут быть разные версии BIOS, в зависимости от ревизии материнской платы одной модели.
Эталонной версией BIOS почти все эмуляторы считают SCPH1001.BIN. Этот BIOS был подробно дизассемблирован и считается "стабильным" для работы в эмуляторах.
Boot
Программа начальной загрузки (RESET)
Исполнение начинается с адреса 0xBFC00000
- Инициализирует недокументированные аппаратные регистры CPU (тайминг и пр.)
- Очищает память и регистры CPU
- Если в PSX присутствует устройство PIO - выполняет его программу загрузки
- Загружает kernel
- Воспроизводит заставку (логотип SONY на белом фоне)
- Запускает процедуру Main, которая либо загружает диск (если он вставлен), либо запускает SHELL
Some reversing of SCPH-1001 BIOS :
// // SCPH1001 Reset // Written likely on assembler // Reset () // 0xBFC00000 { dword.0x1F801010 = 0x13243F; dword.0x1F801060 = 0xB88; // ram_size? // // 20 nops (pipeline reset?) // nop x 20; goto Reset2; } Reset2 () // 0xBFC00150 { // // More registers // dword.0x1F801020 = 0x31125; dword.0x1F801000 = 0x1F000000; dword.0x1F801004 = 0x1F802000; dword.0x1F801008 = 0x13243F; dword.0x1F801014 = 0x200931E1; dword.0x1F801018 = 0x20843; dword.0x1F80100C = 0x3022; dword.0x1F80101C = 0x70777; // // Clear CPU registers // gpr[1...31] = 0; // // B/U Control (Lock ICache?) // dword.0xFFFE0130 = 0x804; // Enable I-Cache + Tag Test Mode // // COP0 // COP0.SR = 0x10000; // Isolate cache from bus // // Clear instruction cache Tag memory (Cache line = 16 bytes) // for ( int Addr=0; Addr<0x1000; Addr += 0x80 ) { dword.[Addr] = 0; dword.[Addr + 0x10] = 0; dword.[Addr + 0x20] = 0; dword.[Addr + 0x30] = 0; dword.[Addr + 0x40] = 0; dword.[Addr + 0x50] = 0; dword.[Addr + 0x60] = 0; dword.[Addr + 0x70] = 0; } // // COP0 // COP0.SR = 0; // // BIU/Cache configuration // dword.0xFFFE0130 = 0x800; // Enable I-Cache // // COP0 // COP0.SR = 0x10000; // Isolate cache from bus // // Clear instruction cache lines // for ( int Addr=0; Addr<0x1000; Addr += 0x80 ) { dword.[Addr] = 0; dword.[Addr + 0x4] = 0; dword.[Addr + 0x8] = 0; dword.[Addr + 0xC] = 0; dword.[Addr + 0x10] = 0; dword.[Addr + 0x14] = 0; dword.[Addr + 0x18] = 0; dword.[Addr + 0x1C] = 0; dword.[Addr + 0x20] = 0; dword.[Addr + 0x24] = 0; dword.[Addr + 0x28] = 0; dword.[Addr + 0x2C] = 0; dword.[Addr + 0x30] = 0; dword.[Addr + 0x34] = 0; dword.[Addr + 0x38] = 0; dword.[Addr + 0x3C] = 0; dword.[Addr + 0x40] = 0; dword.[Addr + 0x44] = 0; dword.[Addr + 0x48] = 0; dword.[Addr + 0x4C] = 0; dword.[Addr + 0x50] = 0; dword.[Addr + 0x54] = 0; dword.[Addr + 0x58] = 0; dword.[Addr + 0x5C] = 0; dword.[Addr + 0x60] = 0; dword.[Addr + 0x64] = 0; dword.[Addr + 0x68] = 0; dword.[Addr + 0x6C] = 0; dword.[Addr + 0x70] = 0; dword.[Addr + 0x74] = 0; dword.[Addr + 0x78] = 0; dword.[Addr + 0x7C] = 0; } // // COP0 // COP0.SR = 0; // // Read memory 8 times // t1 = dword.0xA0000000; t1 = dword.0xA0000000; t1 = dword.0xA0000000; t1 = dword.0xA0000000; t1 = dword.0xA0000000; t1 = dword.0xA0000000; t1 = dword.0xA0000000; t1 = dword.0xA0000000; // // BIU/Cache default configuration // dword.0xFFFE0130 = 0x1E988; // D-Cache as Scratchpad // Enable D-Cache // Enable I-Cache // Enable Bus Grant // No wait state // Enable Read Priority // Enable Load Scheduling // // Reset COP0 regs // COP0.Reg7 = 0; COP0.EntryLo1 = 0; COP0.PageMask = 0; COP0.Wired = 0; COP0.Count = 0; COP0.Compare = 0; COP0.SR = 0; COP0.Cause = 0; // // Clear 0xA0009000 // memset ( 0xA0009000, 0, 0x3160 ); // // Set initial context // CPU.SP = 0x801FFF00; CPU.GP = 0xA0010FF0; CPU.FP = CPU.SP; // // ram_size ? // dword.0x1F801060 = 0xB88; dword.0x60 = 2; dword.0x64 = 0; dword.0x68 = 0xff; // // Mute SPU // word.[0x1F801C00 + 0x180] = 0; // Mainvolume Left word.[0x1F801C00 + 0x182] = 0; // Mainvolume Right word.[0x1F801C00 + 0x184] = 0; // Reverb depth left word.[0x1F801C00 + 0x186] = 0; // Reverb depth right goto Reset3; } // // Following boot code written on C for sure (has C prolog/epilog in code) // Reset3 () // 0xBFC06EC4 { int Present; TraceStep (0xF); word.[0x1F801C00 + 0x186] = 0; word.[0x1F801C00 + 0x184] = 0; word.[0x1F801C00 + 0x182] = 0; word.[0x1F801C00 + 0x180] = 0; Present = CheckPIO (); if ( Present ) ResetPIO (); TraceStep (0xE); dword.0xA000B9B0 = 0; StartKernel (); } TraceStep (a0) // 0xBFC01A60 { Bogus1 (); byte.0x1F802041 = a0 & 0xFF; } Bogus1 () // 0xBFC03990 { dword.0xA000B068 = 0; dword.0xA000B068 = 0; dword.0xA000B068 = 0; dword.0xA000B068 = 0; } // // PIO Support // char Licensed[] = "Licensed by Sony Computer Entertainment Inc."; // 0xBFC0E288 int CheckPIO (void) // 0xBFC0703C { char * Source = Licensed; char * Dest = 0x1F000084; // PIO Header while ( *Source ) { if ( *Source != *Dest ) break; Source++; Dest++; } if ( *Source ) return 0; else return 1; } void ResetPIO (void) // 0xBFC0711C { // // Run init code in PIO Space // dword.0x1F000080 (); } void StartKernel () // 0xBFC06784 { char Config[0x50]; char Exec[0x50]; strcpy ( Config, "cdrom:" ); strcat ( Config, "SYSTEM.CNF;1" ); strcpy ( Exec, "cdrom:" ); strcat ( Exec, "PSX.EXE;1" ); Main ( Config, Exec ); }
Big Main
// // Big Main // #define MUTE_SPU() \ word.[0x1F801C00 + 0x186] = 0; \ word.[0x1F801C00 + 0x184] = 0; \ word.[0x1F801C00 + 0x182] = 0; \ word.[0x1F801C00 + 0x180] = 0; void Main (char *Config, char *Exec ) // 0xBFC067E8 { // // 1 // TraceStep (1); v0 = sub_BFC03968 (); sub_BFC03978 ( v0 & 0xFFFFFBFE ); MUTE_SPU (); // // 2 // TraceStep (2); sub_BFC00420 (); // // 3 // TraceStep (3); sub_BFC042D0 (); sub_BFC042A0 (); sub_BFC0DB10 (); sub_BFC0DB20 (); sub_BFC0D9A0 (); // // 4 // TraceStep (4); MUTE_SPU (); dword.0x1F801074 = 0; dword.0x1F801070 = 0; sub_BFC0DB30 ( dword_B9B0 ); // // 5 // TraceStep (5); printf ( "\n" "PS-X Realtime Kernel Ver.2.5\n" "Copyright 1993,1994 (C) Sony Computer Entertainment Inc. \n" ); // // 6 // TraceStep (6); MUTE_SPU (); sub_BFC02B50 ( 0xA000B940, 0xBFC0E14C, 0xC ); printf ( "KERNEL SETUP!\n" ); sub_BFC0DB40 ( 0xA000E000, 0x2000 ); sub_BFC04610 (4); sub_BFC0DB50 (0) sub_BFC0DB60 (3); sub_BFC04678 ( dword_B944 ); sub_BFC0472C ( 1, dword_B940 ); sub_BFC0DB70 (1); MUTE_SPU (); v0 = sub_BFC02240 ( 0xA000B980 ); if ( v0 ) sub_BFC06FA4 ( 0x385 ); // // 7 // TraceStep (7); sub_BFC06FF0 (); // // 8 // TraceStep (8); dword.0x1F801074 = 0; dword.0x1F801070 = 0; sub_BFC073A0 (); v0 = sub_BFC02240 ( 0xA000B980 ); if ( v0 ) sub_BFC06FA4 ( 0x399 ); v0 = sub_BFC070AC (); if ( v0 == 1 ) sub_BFC07148 (); printf ( "\n" "BOOTSTRAP LOADER Type C Ver 2.1 03-JUL-1994\n" "Copyright 1993,1994 (C) Sony Computer Entertainment Inc.\n" ); v0 = sub_BFC02240 ( 0xA000B980 ); if ( v0 ) sub_BFC06FA4 ( 0x386 ); // // 9 // TraceStep (9); v0 = sub_BFC02240 ( 0xA000B980); if ( v0 ) sub_BFC06FA4 (0x387); var_4 = sub_BFC0D890 ( Config, 1 ); if ( var_4 >= 0 ) { printf ( "setup file : %s\n", Config ); v0 = sub_BFC02240 ( 0xA000B980 ); if ( v0 ) sub_BFC06FA4 ( 0x38F ); v1 = sub_BFC0D8B0 ( var_4, 0xA000B070, 0x800 ); if ( v1 ) { 0xA000B070[v1] = 0; sub_BFC0D8A0 ( var_4 ); v0 = sub_BFC02240 ( 0xA000B980 ); if ( v0 ) sub_BFC06FA4 ( 0x390 ); sub_BFC008A0 ( 0xA000B070, 0xA000B940, 0xA000B8B0 ); } else { sub_BFC02B50 ( 0xA000B940, 0xBFC0E14C, 0xC ); strcpy ( 0xA000B8B0, Exec ); } } else { v0 = sub_BFC02240 ( 0xA000B980 ); if ( v0 ) sub_BFC06FA4 (0x391); byte.0x180 = 0; sub_BFC02B50 ( 0xA000B940, 0xBFC0E14C, 0xC ); strcpy ( 0xA000B8B0, Exec ); } // // // v0 = sub_BFC02240 ( 0xA000B980 ); if ( v0 ) sub_BFC06FA4 ( 0x388 ); sub_BFC06F28 (); printf ( "boot file : %s\n", 0xA000B8B0 ); v0 = sub_BFC02240 ( 0xA000B980 ); if ( v0 ) sub_BFC06FA4 ( 0x389 ); sub_BFC0D850 (); v0 = sub_BFC03A18 (0xA000B8B0, 0xA000B870 ); if ( v0 == 0 ) sub_BFC06FA4 ( 0x38A ); printf ( "EXEC:PC0(%08x) T_ADDR(%08x) T_SIZE(%08x)\n", dword_B870, dword_B878, dword_B87C ); printf ( "boot address : %08x %08x\n" "Execute !\n\n", dword_B870, dword_B948 ); dword_B890 = dword_B948; dword_B894 = 0; printf ( " S_ADDR(%08x) S_SIZE(%08)\n", dword_B948, 0 ); sub_BFC0D960 (); v0 = sub_BFC02240 ( 0xA000B980 ); if ( v0 ) sub_BFC06FA4 ( 0x38B ); sub_BFC0D570 (0xA000B870, 1, 0 ); printf ( "End of Main\n" ); sub_BFC06FA4 (0x38C); }
Kernel (PlayStation OS)
Ядро PS OS резидентно находится в памяти. Доступ к процедурам ядра производится через специальные таблицы (которые находятся по адресам 0xA0, 0xB0, 0xC0).
Второй способ вызова некоторых механизмов ядра - это инструкция Syscall (но набор её функций ограничен, по сути используется только для EnterCriticalSection / ExitCriticalSection)
Также приложениям доступна специальная "Таблица Таблиц" ядра (ToT), через которую программа может получить различные системные описатели и пр.
Выполнение пользовательских программ происходит в режиме CPU Kernel Mode, поскольку одновременно может быть запущен только один "процесс" (исполняемый файл игры).
Shell
Оболочка BIOS - это специальным образом запакованный исполняемый файл формата PS-X EXE, который находится внутри ROM.
Программа начальной загрузки распаковывает его и загружает на лету в RAM перед запуском.
Оболочка запускается если в привод не вставлен игровой диск.