pureikyubu/SRC/HighLevel/Bootrom.cpp
2020-08-09 12:46:30 +03:00

256 lines
9.5 KiB
C++

// BS and BS2 (IPL) simulation.
#include "pch.h"
using namespace Debug;
// The simulation of BS and BS2 is performed with the cache turned off virtually.
static uint32_t default_syscall[] = { // default exception handler
0x2c01004c, // isync
0xac04007c, // sync
0x6400004c, // rfi
};
// load FST
static void ReadFST()
{
#define DOL_LIMIT (4*1024*1024)
#define ROUND32(x) (((uint32_t)(x)+32-1)&~(32-1))
uint32_t bb2[8]; // space for BB2
uint32_t fstAddr, fstOffs, fstSize, fstMaxSize;
// read BB2
DVD::Seek(DVD_BB2_OFFSET);
DVD::Read((uint8_t *)bb2, 32);
// rounding is not important, but present in new apploaders.
// FST memory address is calculated, by adjusting bb[4] with "DOL LIMIT";
// DOL limit is fixed to 4 mb, for most apploaders (in release date range
// from AnimalCrossing to Zelda: Wind Waker).
fstOffs = _BYTESWAP_UINT32(bb2[1]);
fstSize = ROUND32(_BYTESWAP_UINT32(bb2[2]));
fstMaxSize = ROUND32(_BYTESWAP_UINT32(bb2[3]));
fstAddr = _BYTESWAP_UINT32(bb2[4]); // Ignore this
uint32_t ArenaHi = 0;
Gekko::Gekko->ReadWord(0x80000034, &ArenaHi);
ArenaHi -= fstSize;
Gekko::Gekko->WriteWord(0x80000034, ArenaHi);
// load FST into memory
DVD::Seek(fstOffs);
DVD::Read(&mi.ram[ArenaHi & RAMMASK], fstSize);
// save fst configuration in lomem
Gekko::Gekko->WriteWord(0x80000038, ArenaHi);
Gekko::Gekko->WriteWord(0x8000003c, fstMaxSize);
// adjust arenaHi (OSInit will override it anyway, but not home demos)
// arenaLo set to 0
//CPUWriteWord(0x80000030, 0);
//CPUWriteWord(0x80000034, fstAddr);
}
// execute apploader (apploader base is 0x81200000)
// this is exact apploader emulation. it is safe and checked.
static void BootApploader()
{
uint32_t appHeader[8]; // apploader header information
uint32_t appSize; // size of apploader image
uint32_t appEntryPoint;
uint32_t _prolog, _main, _epilog;
uint32_t offs, size, addr; // return of apploader main
// I use prolog/epilog terms here, but Nintendo is using
// something weird, like : appLoaderFunc1 (see Zelda dump - it
// has some compilation garbage parts from bootrom, hehe).
Report( Channel::HLE, "booting apploader..\n");
// set OSReport dummy
Gekko::Gekko->WriteWord(0x81300000, 0x4e800020 /* blr opcode */);
DVD::Seek(DVD_APPLDR_OFFSET); // apploader offset
DVD::Read((uint8_t *)appHeader, 32); // read apploader header
Gekko::GekkoCore::SwapArea(appHeader, 32); // and swap it
// save apploader info
appEntryPoint = appHeader[4];
appSize = appHeader[5];
// load apploader image
DVD::Seek(0x2460);
DVD::Read(&mi.ram[0x81200000 & RAMMASK], appSize);
// set parameters for apploader entrypoint
Gekko::Gekko->regs.gpr[3] = 0x81300004; // save apploader _prolog offset
Gekko::Gekko->regs.gpr[4] = 0x81300008; // main
Gekko::Gekko->regs.gpr[5] = 0x8130000c; // _epilog
// execute entrypoint
Gekko::Gekko->regs.pc = appEntryPoint;
Gekko::Gekko->regs.spr[(int)Gekko::SPR::LR] = 0;
while (Gekko::Gekko->regs.pc)
{
Gekko::Gekko->Step();
}
// get apploader interface offsets
Gekko::Gekko->ReadWord(0x81300004, &_prolog);
Gekko::Gekko->ReadWord(0x81300008, &_main);
Gekko::Gekko->ReadWord(0x8130000c, &_epilog);
Report( Channel::HLE, "apploader interface : init : %08X main : %08X close : %08X\n",
_prolog, _main, _epilog );
// execute apploader prolog
Gekko::Gekko->regs.gpr[3] = 0x81300000; // OSReport callback as parameter
Gekko::Gekko->regs.pc = _prolog;
Gekko::Gekko->regs.spr[(int)Gekko::SPR::LR] = 0;
while (Gekko::Gekko->regs.pc)
{
Gekko::Gekko->Step();
}
// execute apploader main
do
{
// apploader main parameters
Gekko::Gekko->regs.gpr[3] = 0x81300004; // memory address
Gekko::Gekko->regs.gpr[4] = 0x81300008; // size
Gekko::Gekko->regs.gpr[5] = 0x8130000c; // disk offset
Gekko::Gekko->regs.pc = _main;
Gekko::Gekko->regs.spr[(int)Gekko::SPR::LR] = 0;
while (Gekko::Gekko->regs.pc)
{
Gekko::Gekko->Step();
}
Gekko::Gekko->ReadWord(0x81300004, &addr);
Gekko::Gekko->ReadWord(0x81300008, &size);
Gekko::Gekko->ReadWord(0x8130000c, &offs);
if(size)
{
DVD::Seek(offs);
DVD::Read(&mi.ram[addr & RAMMASK], size);
Report( Channel::HLE, "apploader read : offs : %08X size : %08X addr : %08X\n",
offs, size, addr );
}
} while(Gekko::Gekko->regs.gpr[3] != 0);
// execute apploader epilog
Gekko::Gekko->regs.pc = _epilog;
Gekko::Gekko->regs.spr[(int)Gekko::SPR::LR] = 0;
while (Gekko::Gekko->regs.pc)
{
Gekko::Gekko->Step();
}
Gekko::Gekko->regs.pc = Gekko::Gekko->regs.gpr[3];
Report(Channel::Norm, "\n");
}
// RTC -> TBR
static void __SyncTime(bool rtc)
{
if(!rtc)
{
Gekko::Gekko->regs.tb.uval = 0;
return;
}
RTCUpdate();
Report(Channel::HLE, "updating timer value..\n");
int32_t counterBias = (int32_t)_BYTESWAP_UINT32(exi.sram.counterBias);
int32_t rtcValue = exi.rtcVal + counterBias;
Report(Channel::HLE, "counter bias: %i, real-time clock: %i\n", counterBias, exi.rtcVal);
int64_t newTime = (int64_t)rtcValue * CPU_TIMER_CLOCK;
int64_t systemTime;
Gekko::Gekko->ReadDouble(0x800030d8, (uint64_t *)&systemTime);
systemTime += newTime - Gekko::Gekko->regs.tb.sval;
Gekko::Gekko->WriteDouble(0x800030d8, (uint64_t *)&systemTime);
Gekko::Gekko->regs.tb.sval = newTime;
Report(Channel::HLE, "new timer: 0x%llx\n\n", Gekko::Gekko->GetTicks());
}
void BootROM(bool dvd, bool rtc, uint32_t consoleVer)
{
// set initial MMU state, according with BS2/Dolphin OS
for(int sr=0; sr<16; sr++)
{
Gekko::Gekko->regs.sr[sr] = 0x80000000;
}
// DBATs
Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT0U] = 0x80001fff; Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT0L] = 0x00000002; // 0x80000000, 256mb, Write-back cached
Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT1U] = 0xc0001fff; Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT1L] = 0x0000002a; // 0xC0000000, 256mb, Cache inhibited, Guarded
Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT2U] = 0x00000000; Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT2L] = 0x00000000; // undefined
Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT3U] = 0x00000000; Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT3L] = 0x00000000; // undefined
// IBATs
Gekko::Gekko->regs.spr[(int)Gekko::SPR::IBAT0U] = Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT0U];
Gekko::Gekko->regs.spr[(int)Gekko::SPR::IBAT0L] = Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT0L];
Gekko::Gekko->regs.spr[(int)Gekko::SPR::IBAT1U] = Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT1U];
Gekko::Gekko->regs.spr[(int)Gekko::SPR::IBAT1L] = Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT1L];
Gekko::Gekko->regs.spr[(int)Gekko::SPR::IBAT2U] = Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT2U];
Gekko::Gekko->regs.spr[(int)Gekko::SPR::IBAT2L] = Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT2L];
Gekko::Gekko->regs.spr[(int)Gekko::SPR::IBAT3U] = Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT3U];
Gekko::Gekko->regs.spr[(int)Gekko::SPR::IBAT3L] = Gekko::Gekko->regs.spr[(int)Gekko::SPR::DBAT3L];
// MSR MMU bits
Gekko::Gekko->regs.msr |= (MSR_IR | MSR_DR); // enable translation
// page table
Gekko::Gekko->regs.spr[(int)Gekko::SPR::SDR1] = 0;
Gekko::Gekko->regs.msr &= ~MSR_EE; // disable interrupts/DEC
Gekko::Gekko->regs.msr |= MSR_FP; // enable FP
// from gc-linux dev mailing list
Gekko::Gekko->regs.spr[(int)Gekko::SPR::PVR] = 0x00083214;
// RTC -> TBR
__SyncTime(rtc);
// modify important OS low memory variables (lomem) (BS)
Gekko::Gekko->WriteWord(0x8000002c, consoleVer); // console type
Gekko::Gekko->WriteWord(0x80000028, RAMSIZE); // memsize
Gekko::Gekko->WriteWord(0x800000f0, RAMSIZE); // simmemsize
Gekko::Gekko->WriteWord(0x800000f8, CPU_BUS_CLOCK);
Gekko::Gekko->WriteWord(0x800000fc, CPU_CORE_CLOCK);
// install default syscall. not important for Dolphin OS,
// but should be installed to avoid crash on SC opcode.
memcpy( &mi.ram[(int)Gekko::Exception::SYSCALL],
default_syscall,
sizeof(default_syscall) );
// set stack
Gekko::Gekko->regs.gpr[1] = 0x816ffffc;
Gekko::Gekko->regs.gpr[13] = 0x81100000; // Fake sda1
// simulate or boot apploader, if dvd
if(dvd)
{
// read disk ID information to 0x80000000
DVD::Seek(0);
DVD::Read(mi.ram, 32);
// additional PAL/NTSC selection hack for old VIConfigure()
char *id = (char *)mi.ram;
if(id[3] == 'P') Gekko::Gekko->WriteWord(0x800000CC, 1); // set to PAL
else Gekko::Gekko->WriteWord(0x800000CC, 0);
BootApploader();
}
else
{
Gekko::Gekko->WriteWord(0x80000034, Gekko::Gekko->regs.gpr[1] - 0x10000);
ReadFST(); // load FST, for demos
}
}