/* * powerrom_cpu.cpp - Using the 680x0 emulator in PowerMac ROMs for Basilisk II * * Basilisk II (C) 1997-2005 Christian Bauer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include "sysdeps.h" #include "cpu_emulation.h" #include "main.h" #include "emul_op.h" #include "prefs.h" #include "timer.h" #include "user_strings.h" #include "sheep_driver.h" #define DEBUG 0 #include "debug.h" // Save FP regs in Execute68k()? #define SAVE_FP_EXEC_68K 0 // Constants const char ROM_FILE_NAME[] = "PowerROM"; const char KERNEL_AREA_NAME[] = "Macintosh Kernel Data"; const char DR_CACHE_AREA_NAME[] = "Macintosh DR Cache"; const uint32 ROM_BASE = 0x40800000; // Base address of ROM const uint32 ROM_SIZE = 0x00400000; // Size of ROM file const uint32 ROM_AREA_SIZE = 0x00500000; // Size of ROM area const uint32 DR_CACHE_BASE = 0x69000000; // Address of DR cache const uint32 DR_CACHE_SIZE = 0x80000; // Size of DR Cache const uint32 SIG_STACK_SIZE = 8192; // Size of signal stack // PowerPC opcodes const uint32 POWERPC_NOP = 0x60000000; const uint32 POWERPC_ILLEGAL = 0x00000000; const uint32 POWERPC_BLR = 0x4e800020; const uint32 POWERPC_BCTR = 0x4e800420; // Extra Low Memory Globals #define MODE_68K 0 // 68k emulator active #define MODE_EMUL_OP 1 // Within EMUL_OP routine #define XLM_RESET_STACK 0x2800 // Reset stack pointer #define XLM_KERNEL_DATA 0x2804 // Pointer to Kernel Data #define XLM_TOC 0x2808 // TOC pointer of emulator #define XLM_RUN_MODE 0x2810 // Current run mode, see enum above #define XLM_68K_R25 0x2814 // Contents of the 68k emulator's r25 (which contains the interrupt level), saved upon entering EMUL_OP mode, used by Execute68k() and the USR1 signal handler #define XLM_IRQ_NEST 0x2818 // Interrupt disable nesting counter (>0: disabled) #define XLM_PVR 0x281c // Theoretical PVR #define XLM_EMUL_RETURN_PROC 0x2824 // Pointer to EMUL_RETURN routine #define XLM_EXEC_RETURN_PROC 0x2828 // Pointer to EXEC_RETURN routine #define XLM_EMUL_OP_PROC 0x282c // Pointer to EMUL_OP routine #define XLM_EMUL_RETURN_STACK 0x2830 // Stack pointer for EMUL_RETURN // RAM and ROM pointers uint32 RAMBaseMac; // RAM base (Mac address space) uint8 *RAMBaseHost; // RAM base (host address space) uint32 RAMSize; // Size of RAM uint32 ROMBaseMac; // ROM base (Mac address space) uint8 *ROMBaseHost; // ROM base (host address space) uint32 ROMSize; // Size of ROM // Emulator Data struct EmulatorData { uint32 v[0x400]; }; // Kernel Data struct KernelData { uint32 v[0x400]; EmulatorData ed; }; // Exceptions class file_open_error {}; class file_read_error {}; class rom_size_error {}; // Global variables static void *TOC; // TOC pointer static uint32 PVR; // Theoretical PVR static int64 CPUClockSpeed; // Processor clock speed (Hz) static int64 BusClockSpeed; // Bus clock speed (Hz) static system_info SysInfo; // System information static area_id kernel_area = -1; // Kernel Data area ID static KernelData *kernel_data = NULL; // Pointer to Kernel Data static uint32 KernelDataAddr; // Address of Kernel Data static EmulatorData *emulator_data = NULL; static area_id dr_cache_area; // DR Cache area ID static uint32 DRCacheAddr; // Address of DR Cache static struct sigaction sigusr1_action; // Interrupt signal (of emulator thread) static bool ReadyForSignals = false; // Flag: emul_thread ready to receive signals // Prototypes static void sigusr1_handler(int sig, void *arg, vregs *r); // From main_beos.cpp extern int sheep_fd; // fd of sheep driver extern thread_id emul_thread; // Emulator thread /* * Load ROM file (upper 3MB) * * file_open_error: Cannot open ROM file (nor use built-in ROM) * file_read_error: Cannot read ROM file */ // Decode LZSS data static void decode_lzss(const uint8 *src, uint8 *dest, int size) { char dict[0x1000]; int run_mask = 0, dict_idx = 0xfee; for (;;) { if (run_mask < 0x100) { // Start new run if (--size < 0) break; run_mask = *src++ | 0xff00; } bool bit = run_mask & 1; run_mask >>= 1; if (bit) { // Verbatim copy if (--size < 0) break; int c = *src++; dict[dict_idx++] = c; *dest++ = c; dict_idx &= 0xfff; } else { // Copy from dictionary if (--size < 0) break; int idx = *src++; if (--size < 0) break; int cnt = *src++; idx |= (cnt << 4) & 0xf00; cnt = (cnt & 0x0f) + 3; while (cnt--) { char c = dict[idx++]; dict[dict_idx++] = c; *dest++ = c; idx &= 0xfff; dict_idx &= 0xfff; } } } } static void load_rom(void) { // Get rom file path from preferences const char *rom_path = PrefsFindString("powerrom"); // Try to open ROM file BFile file(rom_path ? rom_path : ROM_FILE_NAME, B_READ_ONLY); if (file.InitCheck() != B_NO_ERROR) { // Failed, then ask sheep driver for ROM uint8 *rom = new uint8[ROM_SIZE]; // Reading directly into the area doesn't work ssize_t actual = read(sheep_fd, (void *)rom, ROM_SIZE); if (actual == ROM_SIZE) { // Copy upper 3MB memcpy((void *)(ROM_BASE + 0x100000), rom + 0x100000, ROM_SIZE - 0x100000); delete[] rom; return; } else throw file_open_error(); } printf(GetString(STR_READING_ROM_FILE)); // Get file size off_t rom_size = 0; file.GetSize(&rom_size); uint8 *rom = new uint8[ROM_SIZE]; // Reading directly into the area doesn't work ssize_t actual = file.Read((void *)rom, ROM_SIZE); if (actual == ROM_SIZE) { // Plain ROM image, copy upper 3MB memcpy((void *)(ROM_BASE + 0x100000), rom + 0x100000, ROM_SIZE - 0x100000); delete[] rom; } else { if (strncmp((char *)rom, "", 11) == 0) { // CHRP compressed ROM image D(bug("CHRP ROM image\n")); uint32 lzss_offset, lzss_size; char *s = strstr((char *)rom, "constant lzss-offset"); if (s == NULL) throw rom_size_error(); s -= 7; if (sscanf(s, "%06lx", &lzss_offset) != 1) throw rom_size_error(); s = strstr((char *)rom, "constant lzss-size"); if (s == NULL) throw rom_size_error(); s -= 7; if (sscanf(s, "%06lx", &lzss_size) != 1) throw rom_size_error(); D(bug("Offset of compressed data: %08lx\n", lzss_offset)); D(bug("Size of compressed data: %08lx\n", lzss_size)); D(bug("Uncompressing ROM...\n")); uint8 *decoded = new uint8[ROM_SIZE]; decode_lzss(rom + lzss_offset, decoded, lzss_size); memcpy((void *)(ROM_BASE + 0x100000), decoded + 0x100000, ROM_SIZE - 0x100000); delete[] decoded; delete[] rom; } else if (rom_size != 4*1024*1024) throw rom_size_error(); else throw file_read_error(); } } /* * Patch PowerMac ROM */ // ROM type enum { ROMTYPE_TNT, ROMTYPE_ALCHEMY, ROMTYPE_ZANZIBAR, ROMTYPE_GAZELLE, ROMTYPE_NEWWORLD }; static int ROMType; // Nanokernel boot routine patches static bool patch_nanokernel_boot(void) { uint32 *lp; int i; // Patch ConfigInfo lp = (uint32 *)(ROM_BASE + 0x30d000); lp[0x9c >> 2] = KernelDataAddr; // LA_InfoRecord lp[0xa0 >> 2] = KernelDataAddr; // LA_KernelData lp[0xa4 >> 2] = KernelDataAddr + 0x1000;// LA_EmulatorData lp[0xa8 >> 2] = ROM_BASE + 0x480000; // LA_DispatchTable lp[0xac >> 2] = ROM_BASE + 0x460000; // LA_EmulatorCode lp[0x360 >> 2] = 0; // Physical RAM base (? on NewWorld ROM, this contains -1) lp[0xfd8 >> 2] = ROM_BASE + 0x2a; // 68k reset vector // Skip SR/BAT/SDR init if (ROMType == ROMTYPE_GAZELLE || ROMType == ROMTYPE_NEWWORLD) { lp = (uint32 *)(ROM_BASE + 0x310000); *lp++ = POWERPC_NOP; *lp = 0x38000000; } static const uint32 sr_init_loc[] = {0x3101b0, 0x3101b0, 0x3101b0, 0x3101ec, 0x310200}; lp = (uint32 *)(ROM_BASE + 0x310008); *lp = 0x48000000 | (sr_init_loc[ROMType] - 8) & 0xffff; // b ROM_BASE+0x3101b0 lp = (uint32 *)(ROM_BASE + sr_init_loc[ROMType]); *lp++ = 0x80200000 + XLM_KERNEL_DATA; // lwz r1,(pointer to Kernel Data) *lp++ = 0x3da0dead; // lis r13,0xdead (start of kernel memory) *lp++ = 0x3dc00010; // lis r14,0x0010 (size of page table) *lp = 0x3de00010; // lis r15,0x0010 (size of kernel memory) // Don't read PVR static const uint32 pvr_loc[] = {0x3103b0, 0x3103b4, 0x3103b4, 0x310400, 0x310438}; lp = (uint32 *)(ROM_BASE + pvr_loc[ROMType]); *lp = 0x81800000 + XLM_PVR; // lwz r12,(theoretical PVR) // Set CPU specific data (even if ROM doesn't have support for that CPU) lp = (uint32 *)(ROM_BASE + pvr_loc[ROMType]); if (ntohl(lp[6]) != 0x2c0c0001) return false; uint32 ofs = lp[7] & 0xffff; lp[8] = (lp[8] & 0xffff) | 0x48000000; // beq -> b uint32 loc = (lp[8] & 0xffff) + (uint32)(lp+8) - ROM_BASE; lp = (uint32 *)(ROM_BASE + ofs + 0x310000); switch (PVR >> 16) { case 1: // 601 lp[0] = 0x1000; // Page size lp[1] = 0x8000; // Data cache size lp[2] = 0x8000; // Inst cache size lp[3] = 0x00200020; // Coherency block size/Reservation granule size lp[4] = 0x00010040; // Unified caches/Inst cache line size lp[5] = 0x00400020; // Data cache line size/Data cache block size touch lp[6] = 0x00200020; // Inst cache block size/Data cache block size lp[7] = 0x00080008; // Inst cache assoc/Data cache assoc lp[8] = 0x01000002; // TLB total size/TLB assoc break; case 3: // 603 lp[0] = 0x1000; // Page size lp[1] = 0x2000; // Data cache size lp[2] = 0x2000; // Inst cache size lp[3] = 0x00200020; // Coherency block size/Reservation granule size lp[4] = 0x00000020; // Unified caches/Inst cache line size lp[5] = 0x00200020; // Data cache line size/Data cache block size touch lp[6] = 0x00200020; // Inst cache block size/Data cache block size lp[7] = 0x00020002; // Inst cache assoc/Data cache assoc lp[8] = 0x00400002; // TLB total size/TLB assoc break; case 4: // 604 lp[0] = 0x1000; // Page size lp[1] = 0x4000; // Data cache size lp[2] = 0x4000; // Inst cache size lp[3] = 0x00200020; // Coherency block size/Reservation granule size lp[4] = 0x00000020; // Unified caches/Inst cache line size lp[5] = 0x00200020; // Data cache line size/Data cache block size touch lp[6] = 0x00200020; // Inst cache block size/Data cache block size lp[7] = 0x00040004; // Inst cache assoc/Data cache assoc lp[8] = 0x00800002; // TLB total size/TLB assoc break; // case 5: // 740? case 6: // 603e case 7: // 603ev lp[0] = 0x1000; // Page size lp[1] = 0x4000; // Data cache size lp[2] = 0x4000; // Inst cache size lp[3] = 0x00200020; // Coherency block size/Reservation granule size lp[4] = 0x00000020; // Unified caches/Inst cache line size lp[5] = 0x00200020; // Data cache line size/Data cache block size touch lp[6] = 0x00200020; // Inst cache block size/Data cache block size lp[7] = 0x00040004; // Inst cache assoc/Data cache assoc lp[8] = 0x00400002; // TLB total size/TLB assoc break; case 8: // 750 lp[0] = 0x1000; // Page size lp[1] = 0x8000; // Data cache size lp[2] = 0x8000; // Inst cache size lp[3] = 0x00200020; // Coherency block size/Reservation granule size lp[4] = 0x00000020; // Unified caches/Inst cache line size lp[5] = 0x00200020; // Data cache line size/Data cache block size touch lp[6] = 0x00200020; // Inst cache block size/Data cache block size lp[7] = 0x00080008; // Inst cache assoc/Data cache assoc lp[8] = 0x00800002; // TLB total size/TLB assoc break; case 9: // 604e case 10: // 604ev5 lp[0] = 0x1000; // Page size lp[1] = 0x8000; // Data cache size lp[2] = 0x8000; // Inst cache size lp[3] = 0x00200020; // Coherency block size/Reservation granule size lp[4] = 0x00000020; // Unified caches/Inst cache line size lp[5] = 0x00200020; // Data cache line size/Data cache block size touch lp[6] = 0x00200020; // Inst cache block size/Data cache block size lp[7] = 0x00040004; // Inst cache assoc/Data cache assoc lp[8] = 0x00800002; // TLB total size/TLB assoc break; // case 11: // X704? case 12: // ??? lp[0] = 0x1000; // Page size lp[1] = 0x8000; // Data cache size lp[2] = 0x8000; // Inst cache size lp[3] = 0x00200020; // Coherency block size/Reservation granule size lp[4] = 0x00000020; // Unified caches/Inst cache line size lp[5] = 0x00200020; // Data cache line size/Data cache block size touch lp[6] = 0x00200020; // Inst cache block size/Data cache block size lp[7] = 0x00080008; // Inst cache assoc/Data cache assoc lp[8] = 0x00800002; // TLB total size/TLB assoc break; case 13: // ??? lp[0] = 0x1000; // Page size lp[1] = 0x8000; // Data cache size lp[2] = 0x8000; // Inst cache size lp[3] = 0x00200020; // Coherency block size/Reservation granule size lp[4] = 0x00000020; // Unified caches/Inst cache line size lp[5] = 0x00200020; // Data cache line size/Data cache block size touch lp[6] = 0x00200020; // Inst cache block size/Data cache block size lp[7] = 0x00080008; // Inst cache assoc/Data cache assoc lp[8] = 0x01000004; // TLB total size/TLB assoc break; // case 50: // 821 // case 80: // 860 case 96: // ??? lp[0] = 0x1000; // Page size lp[1] = 0x8000; // Data cache size lp[2] = 0x8000; // Inst cache size lp[3] = 0x00200020; // Coherency block size/Reservation granule size lp[4] = 0x00010020; // Unified caches/Inst cache line size lp[5] = 0x00200020; // Data cache line size/Data cache block size touch lp[6] = 0x00200020; // Inst cache block size/Data cache block size lp[7] = 0x00080008; // Inst cache assoc/Data cache assoc lp[8] = 0x00800004; // TLB total size/TLB assoc break; default: printf("WARNING: Unknown CPU type\n"); break; } // Don't set SPRG3, don't test MQ lp = (uint32 *)(ROM_BASE + loc + 0x20); *lp++ = POWERPC_NOP; lp++; *lp++ = POWERPC_NOP; lp++; *lp = POWERPC_NOP; // Don't read MSR lp = (uint32 *)(ROM_BASE + loc + 0x40); *lp = 0x39c00000; // li r14,0 // Don't write to DEC lp = (uint32 *)(ROM_BASE + loc + 0x70); *lp++ = POWERPC_NOP; loc = (lp[0] & 0xffff) + (uint32)lp - ROM_BASE; // Don't set SPRG3 lp = (uint32 *)(ROM_BASE + loc + 0x2c); *lp = POWERPC_NOP; // Don't read PVR static const uint32 pvr_ofs[] = {0x138, 0x138, 0x138, 0x140, 0x148}; lp = (uint32 *)(ROM_BASE + loc + pvr_ofs[ROMType]); *lp = 0x82e00000 + XLM_PVR; // lwz r23,(theoretical PVR) lp = (uint32 *)(ROM_BASE + loc + 0x170); if (*lp == 0x7eff42a6) // NewWorld ROM *lp = 0x82e00000 + XLM_PVR; // lwz r23,(theoretical PVR) lp = (uint32 *)(ROM_BASE + 0x313134); if (*lp == 0x7e5f42a6) *lp = 0x82400000 + XLM_PVR; // lwz r18,(theoretical PVR) lp = (uint32 *)(ROM_BASE + 0x3131f4); if (*lp == 0x7e5f42a6) // NewWorld ROM *lp = 0x82400000 + XLM_PVR; // lwz r18,(theoretical PVR) // Don't read SDR1 static const uint32 sdr1_ofs[] = {0x174, 0x174, 0x174, 0x17c, 0x19c}; lp = (uint32 *)(ROM_BASE + loc + sdr1_ofs[ROMType]); *lp++ = 0x3d00dead; // lis r8,0xdead (pointer to page table) *lp++ = 0x3ec0001f; // lis r22,0x001f (size of page table) *lp = POWERPC_NOP; // Don't clear page table static const uint32 pgtb_ofs[] = {0x198, 0x198, 0x198, 0x1a0, 0x1c4}; lp = (uint32 *)(ROM_BASE + loc + pgtb_ofs[ROMType]); *lp = POWERPC_NOP; // Don't invalidate TLB static const uint32 tlb_ofs[] = {0x1a0, 0x1a0, 0x1a0, 0x1a8, 0x1cc}; lp = (uint32 *)(ROM_BASE + loc + tlb_ofs[ROMType]); *lp = POWERPC_NOP; // Don't create RAM descriptor table static const uint32 desc_ofs[] = {0x350, 0x350, 0x350, 0x358, 0x37c}; lp = (uint32 *)(ROM_BASE + loc + desc_ofs[ROMType]); *lp = POWERPC_NOP; // Don't load SRs and BATs static const uint32 sr_ofs[] = {0x3d8, 0x3d8, 0x3d8, 0x3e0, 0x404}; lp = (uint32 *)(ROM_BASE + loc + sr_ofs[ROMType]); *lp = POWERPC_NOP; // Don't mess with SRs static const uint32 sr2_ofs[] = {0x312118, 0x312118, 0x312118, 0x312118, 0x3121b4}; lp = (uint32 *)(ROM_BASE + sr2_ofs[ROMType]); *lp = POWERPC_BLR; // Don't check performance monitor static const uint32 pm_ofs[] = {0x313148, 0x313148, 0x313148, 0x313148, 0x313218}; lp = (uint32 *)(ROM_BASE + pm_ofs[ROMType]); while (*lp != 0x7e58eba6) lp++; *lp++ = POWERPC_NOP; while (*lp != 0x7e78eaa6) lp++; *lp++ = POWERPC_NOP; while (*lp != 0x7e59eba6) lp++; *lp++ = POWERPC_NOP; while (*lp != 0x7e79eaa6) lp++; *lp++ = POWERPC_NOP; while (*lp != 0x7e5aeba6) lp++; *lp++ = POWERPC_NOP; while (*lp != 0x7e7aeaa6) lp++; *lp++ = POWERPC_NOP; while (*lp != 0x7e5beba6) lp++; *lp++ = POWERPC_NOP; while (*lp != 0x7e7beaa6) lp++; *lp++ = POWERPC_NOP; while (*lp != 0x7e5feba6) lp++; *lp++ = POWERPC_NOP; while (*lp != 0x7e7feaa6) lp++; *lp++ = POWERPC_NOP; while (*lp != 0x7e5ceba6) lp++; *lp++ = POWERPC_NOP; while (*lp != 0x7e7ceaa6) lp++; *lp++ = POWERPC_NOP; while (*lp != 0x7e5deba6) lp++; *lp++ = POWERPC_NOP; while (*lp != 0x7e7deaa6) lp++; *lp++ = POWERPC_NOP; while (*lp != 0x7e5eeba6) lp++; *lp++ = POWERPC_NOP; while (*lp != 0x7e7eeaa6) lp++; *lp++ = POWERPC_NOP; // Jump to 68k emulator static const uint32 jump68k_ofs[] = {0x40c, 0x40c, 0x40c, 0x414, 0x438}; lp = (uint32 *)(ROM_BASE + loc + jump68k_ofs[ROMType]); *lp++ = 0x80610634; // lwz r3,0x0634(r1) (pointer to Emulator Data) *lp++ = 0x8081119c; // lwz r4,0x119c(r1) (pointer to opcode table) *lp++ = 0x80011184; // lwz r0,0x1184(r1) (pointer to emulator entry) *lp++ = 0x7c0903a6; // mtctr r0 *lp = POWERPC_BCTR; return true; } // 68k emulator patches static bool patch_68k_emul(void) { uint32 *lp; uint32 base; // Overwrite twi instructions static const uint32 twi_loc[] = {0x36e680, 0x36e6c0, 0x36e6c0, 0x36e6c0, 0x36e740}; base = twi_loc[ROMType]; lp = (uint32 *)(ROM_BASE + base); *lp++ = 0x48000000 + 0x36f900 - base; // b 0x36f900 (Emulator start) *lp++ = POWERPC_ILLEGAL; *lp++ = 0x48000000 + 0x36fb00 - base - 8; // b 0x36fb00 (Reset opcode) *lp++ = POWERPC_ILLEGAL; *lp++ = POWERPC_ILLEGAL; *lp++ = POWERPC_ILLEGAL; *lp++ = POWERPC_ILLEGAL; *lp++ = POWERPC_ILLEGAL; *lp++ = POWERPC_ILLEGAL; *lp++ = POWERPC_ILLEGAL; *lp++ = POWERPC_ILLEGAL; *lp++ = POWERPC_ILLEGAL; *lp++ = POWERPC_ILLEGAL; *lp++ = POWERPC_ILLEGAL; *lp++ = POWERPC_ILLEGAL; *lp++ = POWERPC_ILLEGAL; // Set reset stack pointer lp = (uint32 *)(ROM_BASE + base + 0xf0); *lp++ = 0x80200000 + XLM_RESET_STACK; // lwz r1,XLM_RESET_STACK // Install EXEC_RETURN and EMUL_OP opcodes lp = (uint32 *)(ROM_BASE + 0x380000 + (M68K_EXEC_RETURN << 3)); *lp++ = 0x80000000 + XLM_EXEC_RETURN_PROC; // lwz r0,XLM_EXEC_RETURN_PROC *lp++ = 0x4bfb6ffc; // b 0x36f800 for (int i=0; i> 16); // lis r0,xxx *lp++ = 0x60000000 + ((ROM_BASE + 0x46d0a4) & 0xffff); // ori r0,r0,xxx *lp++ = 0x7c0903a6; // mtctr r0 *lp = POWERPC_BCTR; // bctr return true; } // Nanokernel patches static bool patch_nanokernel(void) { uint32 *lp; // Patch 68k emulator trap routine lp = (uint32 *)(ROM_BASE + 0x312994); // Always restore FPU state while (*lp != 0x39260040) lp++; lp--; *lp = 0x48000441; // bl 0x00312dd4 lp = (uint32 *)(ROM_BASE + 0x312dd8); // Don't modify MSR to turn on FPU while (*lp != 0x810600e4) lp++; lp--; *lp++ = POWERPC_NOP; lp += 2; *lp++ = POWERPC_NOP; lp++; *lp++ = POWERPC_NOP; *lp++ = POWERPC_NOP; *lp = POWERPC_NOP; // Patch trap return routine lp = (uint32 *)(ROM_BASE + 0x312c20); while (*lp != 0x7d5a03a6) lp++; *lp++ = 0x7d4903a6; // mtctr r10 *lp++ = 0x7daff120; // mtcr r13 *lp++ = 0x48000000 + 0x8000 - ((uint32)lp & 0xffff); // b ROM_BASE+0x318000 uint32 xlp = (uint32)lp & 0xffff; lp = (uint32 *)(ROM_BASE + 0x312c50); // Replace rfi while (*lp != 0x4c000064) lp++; *lp = POWERPC_BCTR; lp = (uint32 *)(ROM_BASE + 0x318000); *lp++ = 0x81400000 + XLM_IRQ_NEST; // lwz r10,XLM_IRQ_NEST *lp++ = 0x394affff; // subi r10,r10,1 *lp++ = 0x91400000 + XLM_IRQ_NEST; // stw r10,XLM_IRQ_NEST *lp = 0x48000000 + ((xlp - 0x800c) & 0x03fffffc); // b ROM_BASE+0x312c2c return true; } static bool patch_rom(void) { // Detect ROM type if (!memcmp((void *)(ROM_BASE + 0x30d064), "Boot TNT", 8)) ROMType = ROMTYPE_TNT; else if (!memcmp((void *)(ROM_BASE + 0x30d064), "Boot Alchemy", 12)) ROMType = ROMTYPE_ALCHEMY; else if (!memcmp((void *)(ROM_BASE + 0x30d064), "Boot Zanzibar", 13)) ROMType = ROMTYPE_ZANZIBAR; else if (!memcmp((void *)(ROM_BASE + 0x30d064), "Boot Gazelle", 12)) ROMType = ROMTYPE_GAZELLE; else if (!memcmp((void *)(ROM_BASE + 0x30d064), "NewWorld", 8)) ROMType = ROMTYPE_NEWWORLD; else return false; // Apply patches if (!patch_nanokernel_boot()) return false; if (!patch_68k_emul()) return false; if (!patch_nanokernel()) return false; // Copy 68k emulator to 2MB boundary memcpy((void *)(ROM_BASE + ROM_SIZE), (void *)(ROM_BASE + ROM_SIZE - 0x100000), 0x100000); return true; } /* * Initialize 680x0 emulation */ static asm void *get_toc(void) { mr r3,r2 blr } bool Init680x0(void) { char str[256]; // Mac address space = host address space RAMBaseMac = (uint32)RAMBaseHost; ROMBaseMac = (uint32)ROMBaseHost; // Get TOC pointer TOC = get_toc(); // Get system info get_system_info(&SysInfo); switch (SysInfo.cpu_type) { case B_CPU_PPC_601: PVR = 0x00010000; break; case B_CPU_PPC_603: PVR = 0x00030000; break; case B_CPU_PPC_603e: PVR = 0x00060000; break; case B_CPU_PPC_604: PVR = 0x00040000; break; case B_CPU_PPC_604e: PVR = 0x00090000; break; default: PVR = 0x00040000; break; } CPUClockSpeed = SysInfo.cpu_clock_speed; BusClockSpeed = SysInfo.bus_clock_speed; // Delete old areas area_id old_kernel_area = find_area(KERNEL_AREA_NAME); if (old_kernel_area > 0) delete_area(old_kernel_area); area_id old_dr_cache_area = find_area(DR_CACHE_AREA_NAME); if (old_dr_cache_area > 0) delete_area(old_dr_cache_area); // Create area for Kernel Data kernel_data = (KernelData *)0x68ffe000; kernel_area = create_area(KERNEL_AREA_NAME, &kernel_data, B_EXACT_ADDRESS, 0x2000, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); if (kernel_area < 0) { sprintf(str, GetString(STR_NO_KERNEL_DATA_ERR), strerror(kernel_area), kernel_area); ErrorAlert(str); return false; } emulator_data = &kernel_data->ed; KernelDataAddr = (uint32)kernel_data; D(bug("Kernel Data area %ld at %p, Emulator Data at %p\n", kernel_area, kernel_data, emulator_data)); // Load PowerMac ROM (upper 3MB) try { load_rom(); } catch (file_open_error) { ErrorAlert(STR_NO_ROM_FILE_ERR); return false; } catch (file_read_error) { ErrorAlert(STR_ROM_FILE_READ_ERR); return false; } catch (rom_size_error) { ErrorAlert(STR_ROM_SIZE_ERR); return false; } // Install ROM patches if (!patch_rom()) { ErrorAlert("Unsupported PowerMac ROM version"); return false; } // Create area for DR Cache DRCacheAddr = DR_CACHE_BASE; dr_cache_area = create_area(DR_CACHE_AREA_NAME, (void **)&DRCacheAddr, B_EXACT_ADDRESS, DR_CACHE_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); if (dr_cache_area < 0) { sprintf(str, GetString(STR_NO_KERNEL_DATA_ERR), strerror(dr_cache_area), dr_cache_area); ErrorAlert(str); return false; } D(bug("DR Cache area %ld at %p\n", dr_cache_area, DRCacheAddr)); // Initialize Kernel Data memset(kernel_data, 0, sizeof(KernelData)); if (ROMType == ROMTYPE_NEWWORLD) { kernel_data->v[0xc20 >> 2] = RAMSize; kernel_data->v[0xc24 >> 2] = RAMSize; kernel_data->v[0xc30 >> 2] = RAMSize; kernel_data->v[0xc34 >> 2] = RAMSize; kernel_data->v[0xc38 >> 2] = 0x00010020; kernel_data->v[0xc3c >> 2] = 0x00200001; kernel_data->v[0xc40 >> 2] = 0x00010000; kernel_data->v[0xc50 >> 2] = RAMBaseMac; kernel_data->v[0xc54 >> 2] = RAMSize; kernel_data->v[0xf60 >> 2] = PVR; kernel_data->v[0xf64 >> 2] = CPUClockSpeed; kernel_data->v[0xf68 >> 2] = BusClockSpeed; kernel_data->v[0xf6c >> 2] = CPUClockSpeed; } else { kernel_data->v[0xc80 >> 2] = RAMSize; kernel_data->v[0xc84 >> 2] = RAMSize; kernel_data->v[0xc90 >> 2] = RAMSize; kernel_data->v[0xc94 >> 2] = RAMSize; kernel_data->v[0xc98 >> 2] = 0x00010020; kernel_data->v[0xc9c >> 2] = 0x00200001; kernel_data->v[0xca0 >> 2] = 0x00010000; kernel_data->v[0xcb0 >> 2] = RAMBaseMac; kernel_data->v[0xcb4 >> 2] = RAMSize; kernel_data->v[0xf80 >> 2] = PVR; kernel_data->v[0xf84 >> 2] = CPUClockSpeed; kernel_data->v[0xf88 >> 2] = BusClockSpeed; kernel_data->v[0xf8c >> 2] = CPUClockSpeed; } // Initialize extra low memory memset((void *)0x2000, 0, 0x1000); *(uint32 *)XLM_RESET_STACK = 0x2000; // Reset stack pointer *(KernelData **)XLM_KERNEL_DATA = kernel_data;// For trap replacement routines *(void **)XLM_TOC = TOC; // TOC pointer of emulator *(uint32 *)XLM_PVR = PVR; // Theoretical PVR // Clear caches (as we loaded and patched code) clear_caches((void *)ROM_BASE, ROM_AREA_SIZE, B_INVALIDATE_ICACHE | B_FLUSH_DCACHE); return true; } /* * Deinitialize 680x0 emulation */ void Exit680x0(void) { // Delete DR Cache area if (dr_cache_area >= 0) delete_area(dr_cache_area); // Delete Kernel Data area if (kernel_area >= 0) delete_area(kernel_area); } /* * Quit emulator (must only be called from main thread) */ asm void QuitEmulator(void) { lwz r0,XLM_EMUL_RETURN_PROC mtlr r0 blr } /* * Reset and start 680x0 emulation */ static asm void jump_to_rom(register uint32 entry) { // Create stack frame mflr r0 stw r0,8(r1) mfcr r0 stw r0,4(r1) stwu r1,-(56+19*4+18*8)(r1) // Save PowerPC registers stmw r13,56(r1) stfd f14,56+19*4+0*8(r1) stfd f15,56+19*4+1*8(r1) stfd f16,56+19*4+2*8(r1) stfd f17,56+19*4+3*8(r1) stfd f18,56+19*4+4*8(r1) stfd f19,56+19*4+5*8(r1) stfd f20,56+19*4+6*8(r1) stfd f21,56+19*4+7*8(r1) stfd f22,56+19*4+8*8(r1) stfd f23,56+19*4+9*8(r1) stfd f24,56+19*4+10*8(r1) stfd f25,56+19*4+11*8(r1) stfd f26,56+19*4+12*8(r1) stfd f27,56+19*4+13*8(r1) stfd f28,56+19*4+14*8(r1) stfd f29,56+19*4+15*8(r1) stfd f30,56+19*4+16*8(r1) stfd f31,56+19*4+17*8(r1) // Move entry address to ctr, get pointer to Emulator Data mtctr r3 lwz r3,emulator_data(r2) // Skip over EMUL_RETURN routine and get its address bl @1 /* * EMUL_RETURN: Returned from emulator */ // Restore PowerPC registers lwz r1,XLM_EMUL_RETURN_STACK lwz r2,XLM_TOC lmw r13,56(r1) lfd f14,56+19*4+0*8(r1) lfd f15,56+19*4+1*8(r1) lfd f16,56+19*4+2*8(r1) lfd f17,56+19*4+3*8(r1) lfd f18,56+19*4+4*8(r1) lfd f19,56+19*4+5*8(r1) lfd f20,56+19*4+6*8(r1) lfd f21,56+19*4+7*8(r1) lfd f22,56+19*4+8*8(r1) lfd f23,56+19*4+9*8(r1) lfd f24,56+19*4+10*8(r1) lfd f25,56+19*4+11*8(r1) lfd f26,56+19*4+12*8(r1) lfd f27,56+19*4+13*8(r1) lfd f28,56+19*4+14*8(r1) lfd f29,56+19*4+15*8(r1) lfd f30,56+19*4+16*8(r1) lfd f31,56+19*4+17*8(r1) // Exiting from 68k emulator li r0,1 stw r0,XLM_IRQ_NEST li r0,MODE_EMUL_OP stw r0,XLM_RUN_MODE // Return to caller of jump_to_rom() lwz r0,56+19*4+18*8+8(r1) mtlr r0 lwz r0,56+19*4+18*8+4(r1) mtcrf 0xff,r0 addi r1,r1,56+19*4+18*8 blr // Save address of EMUL_RETURN routine for 68k emulator patch @1 mflr r0 stw r0,XLM_EMUL_RETURN_PROC // Skip over EXEC_RETURN routine and get its address bl @2 /* * EXEC_RETURN: Returned from 68k routine executed with Execute68k() */ // Save r25 (contains current 68k interrupt level) stw r25,XLM_68K_R25 // Reentering EMUL_OP mode li r0,MODE_EMUL_OP stw r0,XLM_RUN_MODE // Save 68k registers lwz r4,56+19*4+18*8+12(r1) stw r8,M68kRegisters.d[0](r4) stw r9,M68kRegisters.d[1](r4) stw r10,M68kRegisters.d[2](r4) stw r11,M68kRegisters.d[3](r4) stw r12,M68kRegisters.d[4](r4) stw r13,M68kRegisters.d[5](r4) stw r14,M68kRegisters.d[6](r4) stw r15,M68kRegisters.d[7](r4) stw r16,M68kRegisters.a[0](r4) stw r17,M68kRegisters.a[1](r4) stw r18,M68kRegisters.a[2](r4) stw r19,M68kRegisters.a[3](r4) stw r20,M68kRegisters.a[4](r4) stw r21,M68kRegisters.a[5](r4) stw r22,M68kRegisters.a[6](r4) // Restore PowerPC registers lmw r13,56(r1) #if SAVE_FP_EXEC_68K lfd f14,56+19*4+0*8(r1) lfd f15,56+19*4+1*8(r1) lfd f16,56+19*4+2*8(r1) lfd f17,56+19*4+3*8(r1) lfd f18,56+19*4+4*8(r1) lfd f19,56+19*4+5*8(r1) lfd f20,56+19*4+6*8(r1) lfd f21,56+19*4+7*8(r1) lfd f22,56+19*4+8*8(r1) lfd f23,56+19*4+9*8(r1) lfd f24,56+19*4+10*8(r1) lfd f25,56+19*4+11*8(r1) lfd f26,56+19*4+12*8(r1) lfd f27,56+19*4+13*8(r1) lfd f28,56+19*4+14*8(r1) lfd f29,56+19*4+15*8(r1) lfd f30,56+19*4+16*8(r1) lfd f31,56+19*4+17*8(r1) #endif // Return to caller lwz r0,56+19*4+18*8+8(r1) mtlr r0 addi r1,r1,56+19*4+18*8 blr // Stave address of EXEC_RETURN routine for 68k emulator patch @2 mflr r0 stw r0,XLM_EXEC_RETURN_PROC // Skip over EMUL_OP routine and get its address bl @3 /* * EMUL_OP: Execute native routine, selector in r5 (my own private mode switch) * * 68k registers are stored in a M68kRegisters struct on the stack * which the native routine may read and modify */ // Save r25 (contains current 68k interrupt level) stw r25,XLM_68K_R25 // Entering EMUL_OP mode within 68k emulator li r0,MODE_EMUL_OP stw r0,XLM_RUN_MODE // Create PowerPC stack frame, reserve space for M68kRegisters mr r3,r1 subi r1,r1,56 // Fake "caller" frame rlwinm r1,r1,0,0,29 // Align stack mfcr r0 rlwinm r0,r0,0,11,8 stw r0,4(r1) mfxer r0 stw r0,16(r1) stw r2,12(r1) stwu r1,-(56+16*4+15*8)(r1) lwz r2,XLM_TOC // Save 68k registers stw r8,56+M68kRegisters.d[0](r1) stw r9,56+M68kRegisters.d[1](r1) stw r10,56+M68kRegisters.d[2](r1) stw r11,56+M68kRegisters.d[3](r1) stw r12,56+M68kRegisters.d[4](r1) stw r13,56+M68kRegisters.d[5](r1) stw r14,56+M68kRegisters.d[6](r1) stw r15,56+M68kRegisters.d[7](r1) stw r16,56+M68kRegisters.a[0](r1) stw r17,56+M68kRegisters.a[1](r1) stw r18,56+M68kRegisters.a[2](r1) stw r19,56+M68kRegisters.a[3](r1) stw r20,56+M68kRegisters.a[4](r1) stw r21,56+M68kRegisters.a[5](r1) stw r22,56+M68kRegisters.a[6](r1) stw r3,56+M68kRegisters.a[7](r1) stfd f0,56+16*4+0*8(r1) stfd f1,56+16*4+1*8(r1) stfd f2,56+16*4+2*8(r1) stfd f3,56+16*4+3*8(r1) stfd f4,56+16*4+4*8(r1) stfd f5,56+16*4+5*8(r1) stfd f6,56+16*4+6*8(r1) stfd f7,56+16*4+7*8(r1) mffs f0 stfd f8,56+16*4+8*8(r1) stfd f9,56+16*4+9*8(r1) stfd f10,56+16*4+10*8(r1) stfd f11,56+16*4+11*8(r1) stfd f12,56+16*4+12*8(r1) stfd f13,56+16*4+13*8(r1) stfd f0,56+16*4+14*8(r1) // Execute native routine mr r3,r5 addi r4,r1,56 bl EmulOp // Restore 68k registers lwz r8,56+M68kRegisters.d[0](r1) lwz r9,56+M68kRegisters.d[1](r1) lwz r10,56+M68kRegisters.d[2](r1) lwz r11,56+M68kRegisters.d[3](r1) lwz r12,56+M68kRegisters.d[4](r1) lwz r13,56+M68kRegisters.d[5](r1) lwz r14,56+M68kRegisters.d[6](r1) lwz r15,56+M68kRegisters.d[7](r1) lwz r16,56+M68kRegisters.a[0](r1) lwz r17,56+M68kRegisters.a[1](r1) lwz r18,56+M68kRegisters.a[2](r1) lwz r19,56+M68kRegisters.a[3](r1) lwz r20,56+M68kRegisters.a[4](r1) lwz r21,56+M68kRegisters.a[5](r1) lwz r22,56+M68kRegisters.a[6](r1) lwz r3,56+M68kRegisters.a[7](r1) lfd f13,56+16*4+14*8(r1) lfd f0,56+16*4+0*8(r1) lfd f1,56+16*4+1*8(r1) lfd f2,56+16*4+2*8(r1) lfd f3,56+16*4+3*8(r1) lfd f4,56+16*4+4*8(r1) lfd f5,56+16*4+5*8(r1) lfd f6,56+16*4+6*8(r1) lfd f7,56+16*4+7*8(r1) mtfsf 0xff,f13 lfd f8,56+16*4+8*8(r1) lfd f9,56+16*4+9*8(r1) lfd f10,56+16*4+10*8(r1) lfd f11,56+16*4+11*8(r1) lfd f12,56+16*4+12*8(r1) lfd f13,56+16*4+13*8(r1) // Delete PowerPC stack frame lwz r2,56+16*4+15*8+12(r1) lwz r0,56+16*4+15*8+16(r1) mtxer r0 lwz r0,56+16*4+15*8+4(r1) mtcrf 0xff,r0 mr r1,r3 // Reeintering 68k emulator li r0,MODE_68K stw r0,XLM_RUN_MODE // Set r0 to 0 for 68k emulator li r0,0 // Execute next 68k opcode rlwimi r29,r27,3,13,28 lhau r27,2(r24) mtlr r29 blr // Save address of EMUL_OP routine for 68k emulator patch @3 mflr r0 stw r0,XLM_EMUL_OP_PROC // Save stack pointer for EMUL_RETURN stw r1,XLM_EMUL_RETURN_STACK // Preset registers for ROM boot routine lis r3,0x40b0 // Pointer to ROM boot structure ori r3,r3,0xd000 // 68k emulator is now active li r0,MODE_68K stw r0,XLM_RUN_MODE // Jump to ROM bctr } void Start680x0(void) { // Install interrupt signal handler sigemptyset(&sigusr1_action.sa_mask); sigusr1_action.sa_handler = (__signal_func_ptr)(sigusr1_handler); sigusr1_action.sa_flags = 0; sigusr1_action.sa_userdata = NULL; sigaction(SIGUSR1, &sigusr1_action, NULL); // Install signal stack set_signal_stack(malloc(SIG_STACK_SIZE), SIG_STACK_SIZE); // We're now ready to receive signals ReadyForSignals = true; D(bug("Jumping to ROM\n")); jump_to_rom(ROM_BASE + 0x310000); D(bug("Returned from ROM\n")); // We're no longer ready to receive signals ReadyForSignals = false; } /* * Trigger interrupt */ void TriggerInterrupt(void) { idle_resume(); if (emul_thread > 0 && ReadyForSignals) send_signal(emul_thread, SIGUSR1); } void TriggerNMI(void) { //!! not implemented yet } /* * Execute 68k subroutine * r->a[7] and r->sr are unused! */ static asm void execute_68k(register uint32 addr, register M68kRegisters *r) { // Create stack frame mflr r0 stw r0,8(r1) stw r4,12(r1) stwu r1,-(56+19*4+18*8)(r1) // Save PowerPC registers stmw r13,56(r1) #if SAVE_FP_EXEC_68K stfd f14,56+19*4+0*8(r1) stfd f15,56+19*4+1*8(r1) stfd f16,56+19*4+2*8(r1) stfd f17,56+19*4+3*8(r1) stfd f18,56+19*4+4*8(r1) stfd f19,56+19*4+5*8(r1) stfd f20,56+19*4+6*8(r1) stfd f21,56+19*4+7*8(r1) stfd f22,56+19*4+8*8(r1) stfd f23,56+19*4+9*8(r1) stfd f24,56+19*4+10*8(r1) stfd f25,56+19*4+11*8(r1) stfd f26,56+19*4+12*8(r1) stfd f27,56+19*4+13*8(r1) stfd f28,56+19*4+14*8(r1) stfd f29,56+19*4+15*8(r1) stfd f30,56+19*4+16*8(r1) stfd f31,56+19*4+17*8(r1) #endif // Set up registers for 68k emulator lwz r31,XLM_KERNEL_DATA // Pointer to Kernel Data addi r31,r31,0x1000 // points to Emulator Data li r0,0 mtcrf 0xff,r0 creqv 11,11,11 // Supervisor mode lwz r8,M68kRegisters.d[0](r4) lwz r9,M68kRegisters.d[1](r4) lwz r10,M68kRegisters.d[2](r4) lwz r11,M68kRegisters.d[3](r4) lwz r12,M68kRegisters.d[4](r4) lwz r13,M68kRegisters.d[5](r4) lwz r14,M68kRegisters.d[6](r4) lwz r15,M68kRegisters.d[7](r4) lwz r16,M68kRegisters.a[0](r4) lwz r17,M68kRegisters.a[1](r4) lwz r18,M68kRegisters.a[2](r4) lwz r19,M68kRegisters.a[3](r4) lwz r20,M68kRegisters.a[4](r4) lwz r21,M68kRegisters.a[5](r4) lwz r22,M68kRegisters.a[6](r4) li r23,0 mr r24,r3 lwz r25,XLM_68K_R25 // MSB of SR li r26,0 li r28,0 // VBR lwz r29,0x74(r31) // Pointer to opcode table lwz r30,0x78(r31) // Address of emulator // Reentering 68k emulator li r0,MODE_68K stw r0,XLM_RUN_MODE // Set r0 to 0 for 68k emulator li r0,0 // Execute 68k opcode lha r27,0(r24) rlwimi r29,r27,3,13,28 lhau r27,2(r24) mtlr r29 blr } void Execute68k(uint32 addr, M68kRegisters *r) { uint16 proc[4] = {M68K_JSR, addr >> 16, addr & 0xffff, M68K_EXEC_RETURN}; execute_68k((uint32)proc, r); } /* * Execute MacOS 68k trap * r->a[7] and r->sr are unused! */ void Execute68kTrap(uint16 trap, struct M68kRegisters *r) { uint16 proc[2] = {trap, M68K_EXEC_RETURN}; execute_68k((uint32)proc, r); } /* * USR1 handler */ static void sigusr1_handler(int sig, void *arg, vregs *r) { // Do nothing if interrupts are disabled if ((*(int32 *)XLM_IRQ_NEST) > 0) return; // 68k emulator active? Then trigger 68k interrupt level 1 if (*(uint32 *)XLM_RUN_MODE == MODE_68K) { *(uint16 *)(kernel_data->v[0x67c >> 2]) = 1; r->cr |= kernel_data->v[0x674 >> 2]; } }