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 Sub_BFC06EC4; }
Kernel (PlayStation OS)
Ядро PS OS резидентно находится в памяти. Доступ к процедурам ядра производится через специальные таблицы (которые находятся по адресам 0xA0, 0xB0, 0xC0).
Второй способ вызова некоторых механизмов ядра - это инструкция Syscall (но набор её функций ограничен, по сути используется только для EnterCriticalSection / ExitCriticalSection)
Также приложениям доступна специальная "Таблица Таблиц" ядра (ToT), через которую программа может получить различные системные описатели и пр.
Выполнение пользовательских программ происходит в режиме CPU Kernel Mode, поскольку одновременно может быть запущен только один "процесс" (исполняемый файл игры).
Shell
Оболочка BIOS - это специальным образом запакованный исполняемый файл формата PS-X EXE, который находится внутри ROM.
Программа начальной загрузки распаковывает его и загружает на лету в RAM перед запуском.
Оболочка запускается если в привод не вставлен игровой диск.