Po kilkugodzinnej sesji czytania assemblera, analizy dumpów pamięci i żmudnego przeklikiwania się debuggerem po breakpointach udało mi się rozwiązać problem, który towarzyszył mi od początku projektu.
Problem
Wspominałem o nim w poprzednim poście - objawiał się tekstem "VSync: timeout", a efektem było wyłączanie przerwań systemowych, co z kolei blokowało wykonywanie kodu, gdyż BIOS jak i ładowane programy czekały na eventy (te generowane są przez przerwania).
Przez cały czas błędnie zakładałem, że to kod obsługujący przerwania znajdujący się w BIOSie wyłącza przerwania w systemie po kilku timeoutach. Przejrzałem dokładnie zdisassemblowany kod odpowiadający za wyświetlanie tego komunikatu, ale nie natrafiłem na nigdzie na modyfikację tej flagi. Znalazłem natomiast instrukcje SYSCALL.
Okazuje się, że odpowiada ona za wchodzenie i wychodzenie z sekcji krytycznej - prościej mówiąc wyłącza i włącza przerwania na czas obsługi kodu związanego z przerwaniem. Zajrzałem do kodu emulującego tę instrukcję, a następnie do dokumentacji procesora i dowiedziałem się, że instrukcje SYSCALL i BREAK - w przeciwieństwie do skoków - wykonują się natychmiastowo i nie następuje branch delay slot, który spowodowałby załadowanie kolejnego opcodu po nich.
Rozwiązanie
Problemem był błędnie napisany kod obsługi wyjątków w module procesora, który nie czyścił delay slotu. Rozwiązaniem problemu było dodanie jednej linijki kodu:
Po tej zmianie emulator przestał zawieszać się na komunikacji ze sprzętem i wreszcie był w stanie załadować pierwszy program z płyty:
Timeout nadal się pojawia, ale przestało to wpływać na obsługę przerwań. Jego naprawa wymaga synchronizacji timerów, GPU i CPU.