mirror of
https://github.com/kanjitalk755/macemu.git
synced 2024-08-15 00:29:05 +00:00
![gbeauche](/assets/img/avatar_default.png)
them. So, if someone has BeOS and wants to give it a try, please change and test this new code. Corner case could be a resume_thread() when emul_thread is not suspended. Fixlet to powerrom_cpu: call idle_resume() from TriggerInterrupt().
1368 lines
39 KiB
C++
1368 lines
39 KiB
C++
/*
|
|
* 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 <unistd.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <AppKit.h>
|
|
#include <KernelKit.h>
|
|
#include <StorageKit.h>
|
|
|
|
#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, "<CHRP-BOOT>", 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<M68K_EMUL_OP_MAX-M68K_EMUL_BREAK; i++) {
|
|
*lp++ = 0x38a00000 + i + M68K_EMUL_BREAK; // li r5,M68K_EMUL_OP_*
|
|
*lp++ = 0x4bfb6ffc - i*8; // b 0x36f808
|
|
}
|
|
|
|
// Special handling for M68K_EMUL_OP_SHUTDOWN because Basilisk II is running
|
|
// on the 68k stack and simply quitting would delete the RAM area leaving
|
|
// the stack pointer in unaccessible memory
|
|
lp = (uint32 *)(ROM_BASE + 0x380000 + (M68K_EMUL_OP_SHUTDOWN << 3));
|
|
*lp++ = 0x80000000 + XLM_EMUL_RETURN_PROC; // lwz r0,XLM_EMUL_RETURN_PROC
|
|
*lp++ = 0x4bfb6ffc - (M68K_EMUL_OP_SHUTDOWN - M68K_EXEC_RETURN) * 8; // b 0x36f800
|
|
|
|
// Extra routines for EMUL_RETURN/EXEC_RETURN/EMUL_OP
|
|
lp = (uint32 *)(ROM_BASE + 0x36f800);
|
|
*lp++ = 0x7c0803a6; // mtlr r0
|
|
*lp++ = 0x4e800020; // blr
|
|
|
|
*lp++ = 0x80000000 + XLM_EMUL_OP_PROC; // lwz r0,XLM_EMUL_OP_PROC
|
|
*lp++ = 0x7c0803a6; // mtlr r0
|
|
*lp++ = 0x4e800020; // blr
|
|
|
|
// Extra routine for 68k emulator start
|
|
lp = (uint32 *)(ROM_BASE + 0x36f900);
|
|
*lp++ = 0x7c2903a6; // mtctr r1
|
|
*lp++ = 0x80200000 + XLM_IRQ_NEST; // lwz r1,XLM_IRQ_NEST
|
|
*lp++ = 0x38210001; // addi r1,r1,1
|
|
*lp++ = 0x90200000 + XLM_IRQ_NEST; // stw r1,XLM_IRQ_NEST
|
|
*lp++ = 0x80200000 + XLM_KERNEL_DATA;// lwz r1,XLM_KERNEL_DATA
|
|
*lp++ = 0x90c10018; // stw r6,0x18(r1)
|
|
*lp++ = 0x7cc902a6; // mfctr r6
|
|
*lp++ = 0x90c10004; // stw r6,$0004(r1)
|
|
*lp++ = 0x80c1065c; // lwz r6,$065c(r1)
|
|
*lp++ = 0x90e6013c; // stw r7,$013c(r6)
|
|
*lp++ = 0x91060144; // stw r8,$0144(r6)
|
|
*lp++ = 0x9126014c; // stw r9,$014c(r6)
|
|
*lp++ = 0x91460154; // stw r10,$0154(r6)
|
|
*lp++ = 0x9166015c; // stw r11,$015c(r6)
|
|
*lp++ = 0x91860164; // stw r12,$0164(r6)
|
|
*lp++ = 0x91a6016c; // stw r13,$016c(r6)
|
|
*lp++ = 0x7da00026; // mfcr r13
|
|
*lp++ = 0x80e10660; // lwz r7,$0660(r1)
|
|
*lp++ = 0x7d8802a6; // mflr r12
|
|
*lp++ = 0x50e74001; // rlwimi. r7,r7,8,$80000000
|
|
*lp++ = 0x814105f0; // lwz r10,0x05f0(r1)
|
|
*lp++ = 0x7d4803a6; // mtlr r10
|
|
*lp++ = 0x7d8a6378; // mr r10,r12
|
|
*lp++ = 0x3d600002; // lis r11,0x0002
|
|
*lp++ = 0x616bf072; // ori r11,r11,0xf072 (MSR)
|
|
*lp++ = 0x50e7deb4; // rlwimi r7,r7,27,$00000020
|
|
*lp++ = 0x4e800020; // blr
|
|
|
|
// Extra routine for Reset opcode
|
|
lp = (uint32 *)(ROM_BASE + 0x36fc00);
|
|
*lp++ = 0x7c2903a6; // mtctr r1
|
|
*lp++ = 0x80200000 + XLM_IRQ_NEST; // lwz r1,XLM_IRQ_NEST
|
|
*lp++ = 0x38210001; // addi r1,r1,1
|
|
*lp++ = 0x90200000 + XLM_IRQ_NEST; // stw r1,XLM_IRQ_NEST
|
|
*lp++ = 0x80200000 + XLM_KERNEL_DATA;// lwz r1,XLM_KERNEL_DATA
|
|
*lp++ = 0x90c10018; // stw r6,0x18(r1)
|
|
*lp++ = 0x7cc902a6; // mfctr r6
|
|
*lp++ = 0x90c10004; // stw r6,$0004(r1)
|
|
*lp++ = 0x80c1065c; // lwz r6,$065c(r1)
|
|
*lp++ = 0x90e6013c; // stw r7,$013c(r6)
|
|
*lp++ = 0x91060144; // stw r8,$0144(r6)
|
|
*lp++ = 0x9126014c; // stw r9,$014c(r6)
|
|
*lp++ = 0x91460154; // stw r10,$0154(r6)
|
|
*lp++ = 0x9166015c; // stw r11,$015c(r6)
|
|
*lp++ = 0x91860164; // stw r12,$0164(r6)
|
|
*lp++ = 0x91a6016c; // stw r13,$016c(r6)
|
|
*lp++ = 0x7da00026; // mfcr r13
|
|
*lp++ = 0x80e10660; // lwz r7,$0660(r1)
|
|
*lp++ = 0x7d8802a6; // mflr r12
|
|
*lp++ = 0x50e74001; // rlwimi. r7,r7,8,$80000000
|
|
*lp++ = 0x814105f4; // lwz r10,0x05f8(r1)
|
|
*lp++ = 0x7d4803a6; // mtlr r10
|
|
*lp++ = 0x7d8a6378; // mr r10,r12
|
|
*lp++ = 0x3d600002; // lis r11,0x0002
|
|
*lp++ = 0x616bf072; // ori r11,r11,0xf072 (MSR)
|
|
*lp++ = 0x50e7deb4; // rlwimi r7,r7,27,$00000020
|
|
*lp++ = 0x4e800020; // blr
|
|
|
|
// Patch DR emulator to jump to right address when an interrupt occurs
|
|
lp = (uint32 *)(ROM_BASE + 0x370000);
|
|
while (lp < (uint32 *)(ROM_BASE + 0x380000)) {
|
|
if (*lp == 0x4ca80020) // bclr 5,8
|
|
goto dr_found;
|
|
lp++;
|
|
}
|
|
D(bug("DR emulator patch location not found\n"));
|
|
return false;
|
|
dr_found:
|
|
lp++;
|
|
*lp = 0x48000000 + 0xf000 - ((uint32)lp & 0xffff); // b DR_CACHE_BASE+0x1f000
|
|
lp = (uint32 *)(ROM_BASE + 0x37f000);
|
|
*lp++ = 0x3c000000 + ((ROM_BASE + 0x46d0a4) >> 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];
|
|
}
|
|
}
|