Pisząc blog, w którym opisuje proces tworzenia emulatora dla konsoli Playstation wypada mi przybliżyć jak ten sprzęt jest zbudowany. Patrząc na specyfikację przypomina on komputery, które pojawiały się w tamtych czasach (1994), jednak niektóre rozwiązania wybrane przez twórców - szczególnie te związane z generowaniem grafiki 3D - nie przetrwały do dzisiejszych czasów.
Schemat
Powyższy schemat blokowy ukazuje architekturę konsoli widzianą z poziomu kodu. W rzeczywistości wiele bloków takich jak Interrupts , DMA czy MDEC fizycznie znajdują się w procesorze. Dla programów oraz z punktu widzenia emulacji są to oddzielne urządzenia w przestrzeni adresowej (tzw. MMIO - Memory Mapped Input Output), dlatego takie rozdzielenie ma sens.
CPU
Procesor konsoli oparty jest o architekturę MIPS. Ze względu na rolę w całym systemie poświęcę mu osobny post. W CPU znajdują się dwie mniejsze jednostki:
- COP0 - koprocesor systemowy, układ zajmujący się wyjątkami/przerwaniami, zarządzaniem user/kernel mode (nie używane w konsoli). Podstawowa implementacja jest wymagana do działania przerwań. Układ narysowałem wewnątrz CPU, ponieważ nie jest dostępny na magistrali, a komunikacja z nim odbywa się za pomocą specjalnych instrukcji MFC0 (Move From Co-processor 0) oraz MTC0 (Move To Co-processor 0)
- COP2 (GTE) - Geometry Transformation Engine, jest to wydzielona część procesora do operacji na macierzach i wektorach - wszystkie transformacje związane z grafiką 3d wykonywane są właśnie tutaj. Operacje odbywają się na liczbach całkowitych, a nie zmiennoprzecinkowych jak dzieje się to dzisiaj - wiąże się to specyficznym wyglądem gier na tej konsoli. Jak w przypadku COP0 procesor ma specjalne instrukcje do komunikacji z nim.
Pamięć
- Scratchpad - w rzeczywistości jest to cache procesora dla danych (1kB), jednak tutaj on wykorzystywany jako pamięć o szybkim dostępie.
- RAM - pamięć główna, do dyspozycji 2MB
- ROM (BIOS) - pamięć o wielkości 512kB - znajdują się tam kernel konsoli, część biblioteki standardowej C i program startowy wyświetlający ładne logo i ładujący program z płyty.
- Expansion - port z tyłu konsoli, nie używany oficjalnie. Urządzenia typu Game Shark wykorzystywały go, aby umożliwić wprowadzanie kodów do gier. Zazwyczaj pod ten port podpinana była dodatkowa pamięć ROM.
Ciekawostka - aby konsola uruchomiła kod zamiast tego znajdującego się w BIOSie wystarczyło na początku ROMu zamieścić entry point programu oraz tekst "Licensed by Sony Computer Entertainment Inc." - zabezpieczenia w tamtych czasach nie były skomplikowane 🙂
Urządzenia
- GPU - najważniejsza część systemu, odpowiada za rysowanie kolorowych lub oteksturowanych linii, trójkątów i prostokątów. GPU operuje w całości w 2D i na liczbach całkowitych - wszystkie transformacje są wcześniej wykonywane przez procesor oraz GTE. Ma swój VRAM, do którego CPU nie ma bezpośredniego dostępu.
- CDROM - zajmuje się odczytem z CD i dekodowaniem muzyki (jest połączony z SPU). Interakcja z nim odbywa się na niskopoziomowych instrukcjach typu "przeczytaj n-ty sektor" lub w przypadku Audio CD "odtwarzaj następną ścieżkę". Samą obsługą plików na płycie musi zająć się procesor. Zabezpieczenie przed kopiowaniem gier znajduje się właśnie w tym miejscu - procesor nie otrzyma danych, gdy płyta nie jest oryginalna.
- Interrupts - służy maskowaniu i sprawdzaniu stanu danych przerwań (IRQ). Prawie każdy z wymienionych tutaj bloków mógł zgłosić przerwanie, które w zależności od ustawień było realizowane przez konsolę.
- DMA (Direct Memory Access) - układ odciążający procesor przy transferze dużej ilości danych. Przykładami są kopiowanie grafiki z RAM do VRAM czy odczyt z CDROM do RAM.
- Timers - 3 konfigurowalne liczniki zliczające różne wartości - ilość wyrysowanych linii na ekranie, pikseli lub cykli procesora. W tym momencie nie potrafię napisać jak są wykorzystywane przez gry, BIOS natomiast używa ich do synchronizacji z układem graficznym.
- Controllers and Memory card - rejestry odpowiadające konfiguracji szyny SPI, która odpowiada za komunikację z urządzeniami peryferyjnymi. Wszystkie 4 porty z przodu dzielą te same linie komunikacyjne.
- Serial - port szeregowy znajdujący się z tyłu konsoli. Garstka gier używała go do łączenia dwóch konsol ze sobą dostarczając specjalny tryb multiplayer. Nie będę zajmował się implementacją tej części.
- MDEC (Macroblock Decoder)- układ dekodujący filmy używany przez konsolę, można go porównać do sprzętowego dekodera JPEGów. Nie jest konieczny do rozruchu konsoli.
- SPU - jednostka przetwarzania dźwięku, chyba najbardziej skomplikowany z układów. Umożliwia zarówno dekodowanie skompresowanego (ADPCM) dźwięku jak i generowanie muzyki opartej na kanałach i próbkach (coś w rodzaju MIDI lub MOD). Posiada swoją pamięć do której CPU nie ma bezpośredniego dostępu. Nie jest konieczny do uruchomienia gier, w przyszłości chciałbym zaimplementować chociaż podstawową obsługę dźwięku.
Implementacja
W celu uruchomienia kodu na emulowanym systemie wiele z wyżej wymienionych układów nie wymaga całkowitej emulacji - w przypadku SPU wystarczy zasymulować poprawne zwracanie statusu, aby program nie zawieszał się w momencie komunikacji. W ten sposób potraktowałem początkowo wiele części systemu tworząc mocki, aby skupić się na implementacji CPU lub GPU.