/* * main_beos.cpp - Startup code for BeOS * * Basilisk II (C) 1997-2008 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 #include #include #include #include "sysdeps.h" #include "cpu_emulation.h" #include "xpram.h" #include "timer.h" #include "video.h" #include "rom_patches.h" #include "prefs.h" #include "prefs_editor.h" #include "sys.h" #include "user_strings.h" #include "version.h" #include "main.h" #include "sheep_driver.h" #define DEBUG 0 #include "debug.h" // Constants const char APP_SIGNATURE[] = "application/x-vnd.cebix-BasiliskII"; const char ROM_FILE_NAME[] = "ROM"; const char RAM_AREA_NAME[] = "Macintosh RAM"; const char ROM_AREA_NAME[] = "Macintosh ROM"; const uint32 MSG_START = 'strt'; // Emulator start message const uint32 ROM_AREA_SIZE = 0x500000; // Enough to hold PowerMac ROM (for powerrom_cpu) // Prototypes #if __POWERPC__ static void sigsegv_handler(vregs *r); #endif // Application object class BasiliskII : public BApplication { public: BasiliskII() : BApplication(APP_SIGNATURE) { // Find application directory and cwd to it app_info the_info; GetAppInfo(&the_info); BEntry the_file(&the_info.ref); BEntry the_dir; the_file.GetParent(&the_dir); BPath the_path; the_dir.GetPath(&the_path); chdir(the_path.Path()); // Initialize other variables rom_area = ram_area = -1; xpram_thread = tick_thread = -1; xpram_thread_active = true; tick_thread_active = true; AllowQuitting = true; } virtual void ReadyToRun(void); virtual void MessageReceived(BMessage *msg); void StartEmulator(void); virtual bool QuitRequested(void); virtual void Quit(void); thread_id xpram_thread; // XPRAM watchdog thread_id tick_thread; // 60Hz thread volatile bool xpram_thread_active; // Flag for quitting the XPRAM thread volatile bool tick_thread_active; // Flag for quitting the 60Hz thread bool AllowQuitting; // Flag: Alt-Q quitting allowed private: static status_t emul_func(void *arg); static status_t tick_func(void *arg); static status_t xpram_func(void *arg); static void sigsegv_invoc(int sig, void *arg, vregs *r); void init_rom(void); void load_rom(void); area_id rom_area; // ROM area ID area_id ram_area; // RAM area ID struct sigaction sigsegv_action; // Data access exception signal (of emulator thread) // Exceptions class area_error {}; class file_open_error {}; class file_read_error {}; class rom_size_error {}; char* vmdir; }; static BasiliskII *the_app; // CPU and FPU type, addressing mode int CPUType; bool CPUIs68060; int FPUType; bool TwentyFourBitAddressing; // Global variables for PowerROM CPU thread_id emul_thread = -1; // Emulator thread #if __POWERPC__ int sheep_fd = -1; // fd of sheep driver #endif /* * Create application object and start it */ int main(int argc, char **argv) { the_app = new BasiliskII(); the_app->Run(); delete the_app; return 0; } /* * Run application */ void BasiliskII::ReadyToRun(void) { // Initialize variables RAMBaseHost = NULL; ROMBaseHost = NULL; srand(real_time_clock()); tzset(); // Print some info printf(GetString(STR_ABOUT_TEXT1), VERSION_MAJOR, VERSION_MINOR); printf(" %s\n", GetString(STR_ABOUT_TEXT2)); // Delete old areas area_id old_ram_area = find_area(RAM_AREA_NAME); if (old_ram_area > 0) delete_area(old_ram_area); area_id old_rom_area = find_area(ROM_AREA_NAME); if (old_rom_area > 0) delete_area(old_rom_area); // Read preferences int argc = 0; char **argv = NULL; PrefsInit(vmdir, argc, argv); // Init system routines SysInit(); // Show preferences editor (or start emulator directly) if (!PrefsFindBool("nogui")) PrefsEditor(); else PostMessage(MSG_START); } /* * Message received */ void BasiliskII::MessageReceived(BMessage *msg) { switch (msg->what) { case MSG_START: StartEmulator(); break; default: BApplication::MessageReceived(msg); } } /* * Start emulator */ void BasiliskII::StartEmulator(void) { char str[256]; #if REAL_ADDRESSING // Open sheep driver and remap low memory sheep_fd = open("/dev/sheep", 0); if (sheep_fd < 0) { sprintf(str, GetString(STR_NO_SHEEP_DRIVER_ERR), strerror(sheep_fd), sheep_fd); ErrorAlert(str); PostMessage(B_QUIT_REQUESTED); return; } status_t res = ioctl(sheep_fd, SHEEP_UP); if (res < 0) { sprintf(str, GetString(STR_SHEEP_UP_ERR), strerror(res), res); ErrorAlert(str); PostMessage(B_QUIT_REQUESTED); return; } #endif // Create area for Mac RAM RAMSize = PrefsFindInt32("ramsize") & 0xfff00000; // Round down to 1MB boundary if (RAMSize < 1024*1024) { WarningAlert(GetString(STR_SMALL_RAM_WARN)); RAMSize = 1024*1024; } RAMBaseHost = (uint8 *)0x10000000; ram_area = create_area(RAM_AREA_NAME, (void **)&RAMBaseHost, B_BASE_ADDRESS, RAMSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); if (ram_area < 0) { ErrorAlert(STR_NO_RAM_AREA_ERR); PostMessage(B_QUIT_REQUESTED); return; } D(bug("RAM area %ld at %p\n", ram_area, RAMBaseHost)); // Create area and load Mac ROM try { init_rom(); } catch (area_error) { ErrorAlert(STR_NO_ROM_AREA_ERR); PostMessage(B_QUIT_REQUESTED); return; } catch (file_open_error) { ErrorAlert(STR_NO_ROM_FILE_ERR); PostMessage(B_QUIT_REQUESTED); return; } catch (file_read_error) { ErrorAlert(STR_ROM_FILE_READ_ERR); PostMessage(B_QUIT_REQUESTED); return; } catch (rom_size_error) { ErrorAlert(STR_ROM_SIZE_ERR); PostMessage(B_QUIT_REQUESTED); return; } // Initialize everything if (!InitAll(NULL)) { PostMessage(B_QUIT_REQUESTED); return; } // Write protect ROM set_area_protection(rom_area, B_READ_AREA); // Disallow quitting with Alt-Q from now on AllowQuitting = false; // Start XPRAM watchdog thread xpram_thread = spawn_thread(xpram_func, "XPRAM Watchdog", B_LOW_PRIORITY, this); resume_thread(xpram_thread); // Start 60Hz interrupt tick_thread = spawn_thread(tick_func, "60Hz", B_REAL_TIME_PRIORITY, this); resume_thread(tick_thread); // Start emulator thread emul_thread = spawn_thread(emul_func, "MacOS", B_NORMAL_PRIORITY, this); resume_thread(emul_thread); } /* * Quit emulator */ void QuitEmulator(void) { the_app->AllowQuitting = true; be_app->PostMessage(B_QUIT_REQUESTED); exit_thread(0); } bool BasiliskII::QuitRequested(void) { if (AllowQuitting) return BApplication::QuitRequested(); else return false; } void BasiliskII::Quit(void) { status_t l; // Stop 60Hz interrupt if (tick_thread > 0) { tick_thread_active = false; wait_for_thread(tick_thread, &l); } // Wait for emulator thread to finish if (emul_thread > 0) wait_for_thread(emul_thread, &l); // Exit 680x0 emulation Exit680x0(); // Stop XPRAM watchdog thread if (xpram_thread > 0) { xpram_thread_active = false; suspend_thread(xpram_thread); // Wake thread up from snooze() snooze(1000); resume_thread(xpram_thread); wait_for_thread(xpram_thread, &l); } // Deinitialize everything ExitAll(); // Delete ROM area if (rom_area >= 0) delete_area(rom_area); // Delete RAM area if (ram_area >= 0) delete_area(ram_area); #if REAL_ADDRESSING // Unmap low memory and close memory mess driver if (sheep_fd >= 0) { ioctl(sheep_fd, SHEEP_DOWN); close(sheep_fd); } #endif // Exit system routines SysExit(); // Exit preferences PrefsExit(); BApplication::Quit(); } /* * Create area for ROM (sets rom_area) and load ROM file * * area_error : Cannot create area * file_open_error: Cannot open ROM file * file_read_error: Cannot read ROM file */ void BasiliskII::init_rom(void) { // Create area for ROM ROMBaseHost = (uint8 *)0x40800000; rom_area = create_area(ROM_AREA_NAME, (void **)&ROMBaseHost, B_BASE_ADDRESS, ROM_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); if (rom_area < 0) throw area_error(); D(bug("ROM area %ld at %p\n", rom_area, ROMBaseHost)); // Load ROM load_rom(); } /* * Load ROM file * * file_open_error: Cannot open ROM file * file_read_error: Cannot read ROM file */ void BasiliskII::load_rom(void) { // Get rom file path from preferences const char *rom_path = PrefsFindString("rom"); // Try to open ROM file BFile file(rom_path ? rom_path : ROM_FILE_NAME, B_READ_ONLY); if (file.InitCheck() != B_NO_ERROR) throw file_open_error(); printf(GetString(STR_READING_ROM_FILE)); // Is the ROM size correct? off_t rom_size = 0; file.GetSize(&rom_size); if (rom_size != 64*1024 && rom_size != 128*1024 && rom_size != 256*1024 && rom_size != 512*1024 && rom_size != 1024*1024) throw rom_size_error(); 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) memcpy(ROMBaseHost, rom, rom_size); delete[] rom; if (actual != rom_size) throw file_read_error(); ROMSize = rom_size; } /* * Emulator thread function */ status_t BasiliskII::emul_func(void *arg) { BasiliskII *obj = (BasiliskII *)arg; #if __POWERPC__ // Install data access signal handler sigemptyset(&obj->sigsegv_action.sa_mask); obj->sigsegv_action.sa_handler = (__signal_func_ptr)(obj->sigsegv_invoc); obj->sigsegv_action.sa_flags = 0; obj->sigsegv_action.sa_userdata = arg; sigaction(SIGSEGV, &obj->sigsegv_action, NULL); #endif // Exceptions will send signals disable_debugger(true); // Start 68k and jump to ROM boot routine Start680x0(); // Quit program obj->AllowQuitting = true; be_app->PostMessage(B_QUIT_REQUESTED); return 0; } /* * Code was patched, flush caches if neccessary (i.e. when using a real 680x0 * or a dynamically recompiling emulator) */ void FlushCodeCache(void *start, uint32 size) { } /* * Mutexes */ struct B2_mutex { int dummy; //!! }; B2_mutex *B2_create_mutex(void) { return new B2_mutex; } void B2_lock_mutex(B2_mutex *mutex) { } void B2_unlock_mutex(B2_mutex *mutex) { } void B2_delete_mutex(B2_mutex *mutex) { delete mutex; } /* * Interrupt flags (must be handled atomically!) */ uint32 InterruptFlags = 0; void SetInterruptFlag(uint32 flag) { atomic_or((int32 *)&InterruptFlags, flag); } void ClearInterruptFlag(uint32 flag) { atomic_and((int32 *)&InterruptFlags, ~flag); } /* * 60Hz thread (really 60.15Hz) */ status_t BasiliskII::tick_func(void *arg) { BasiliskII *obj = (BasiliskII *)arg; int tick_counter = 0; bigtime_t current = system_time(); while (obj->tick_thread_active) { // Wait current += 16625; snooze_until(current, B_SYSTEM_TIMEBASE); // Pseudo Mac 1Hz interrupt, update local time if (++tick_counter > 60) { tick_counter = 0; WriteMacInt32(0x20c, TimerDateTime()); SetInterruptFlag(INTFLAG_1HZ); TriggerInterrupt(); } // Trigger 60Hz interrupt SetInterruptFlag(INTFLAG_60HZ); TriggerInterrupt(); } return 0; } /* * XPRAM watchdog thread (saves XPRAM every minute) */ status_t BasiliskII::xpram_func(void *arg) { uint8 last_xpram[XPRAM_SIZE]; memcpy(last_xpram, XPRAM, XPRAM_SIZE); while (((BasiliskII *)arg)->xpram_thread_active) { snooze(60*1000000); if (memcmp(last_xpram, XPRAM, XPRAM_SIZE)) { memcpy(last_xpram, XPRAM, XPRAM_SIZE); SaveXPRAM(); } } return 0; } /* * Display error alert */ void ErrorAlert(const char *text) { if (PrefsFindBool("nogui")) { printf(GetString(STR_SHELL_ERROR_PREFIX), text); return; } VideoQuitFullScreen(); char str[256]; sprintf(str, GetString(STR_GUI_ERROR_PREFIX), text); BAlert *alert = new BAlert(GetString(STR_ERROR_ALERT_TITLE), str, GetString(STR_QUIT_BUTTON), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); alert->Go(); } /* * Display warning alert */ void WarningAlert(const char *text) { if (PrefsFindBool("nogui")) { printf(GetString(STR_SHELL_WARNING_PREFIX), text); return; } char str[256]; sprintf(str, GetString(STR_GUI_WARNING_PREFIX), text); BAlert *alert = new BAlert(GetString(STR_WARNING_ALERT_TITLE), str, GetString(STR_OK_BUTTON), NULL, NULL, B_WIDTH_AS_USUAL, B_INFO_ALERT); alert->Go(); } /* * Display choice alert */ bool ChoiceAlert(const char *text, const char *pos, const char *neg) { char str[256]; sprintf(str, GetString(STR_GUI_WARNING_PREFIX), text); BAlert *alert = new BAlert(GetString(STR_WARNING_ALERT_TITLE), str, pos, neg, NULL, B_WIDTH_AS_USUAL, B_INFO_ALERT); return alert->Go() == 0; } /* * SEGV handler */ #if __POWERPC__ static uint32 segv_r[32]; asm void BasiliskII::sigsegv_invoc(register int sig, register void *arg, register vregs *r) { mflr r0 stw r0,8(r1) stwu r1,-56(r1) lwz r3,segv_r(r2) stmw r13,13*4(r3) mr r3,r5 bl sigsegv_handler lwz r3,segv_r(r2) lmw r13,13*4(r3) lwz r0,56+8(r1) mtlr r0 addi r1,r1,56 blr } static void sigsegv_handler(vregs *r) { // Fetch volatile registers segv_r[0] = r->r0; segv_r[1] = r->r1; segv_r[2] = r->r2; segv_r[3] = r->r3; segv_r[4] = r->r4; segv_r[5] = r->r5; segv_r[6] = r->r6; segv_r[7] = r->r7; segv_r[8] = r->r8; segv_r[9] = r->r9; segv_r[10] = r->r10; segv_r[11] = r->r11; segv_r[12] = r->r12; // Get opcode and divide into fields uint32 opcode = *(uint32 *)r->pc; uint32 primop = opcode >> 26; uint32 exop = (opcode >> 1) & 0x3ff; uint32 ra = (opcode >> 16) & 0x1f; uint32 rb = (opcode >> 11) & 0x1f; uint32 rd = (opcode >> 21) & 0x1f; uint32 imm = opcode & 0xffff; // Analyze opcode enum { TYPE_UNKNOWN, TYPE_LOAD, TYPE_STORE } transfer_type = TYPE_UNKNOWN; enum { SIZE_UNKNOWN, SIZE_BYTE, SIZE_HALFWORD, SIZE_WORD } transfer_size = SIZE_UNKNOWN; enum { MODE_UNKNOWN, MODE_NORM, MODE_U, MODE_X, MODE_UX } addr_mode = MODE_UNKNOWN; switch (primop) { case 31: switch (exop) { case 23: // lwzx transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_X; break; case 55: // lwzux transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break; case 87: // lbzx transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break; case 119: // lbzux transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break; case 151: // stwx transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_X; break; case 183: // stwux transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break; case 215: // stbx transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break; case 247: // stbux transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break; case 279: // lhzx transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break; case 311: // lhzux transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break; case 343: // lhax transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break; case 375: // lhaux transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break; case 407: // sthx transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break; case 439: // sthux transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break; } break; case 32: // lwz transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break; case 33: // lwzu transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_U; break; case 34: // lbz transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break; case 35: // lbzu transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break; case 36: // stw transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break; case 37: // stwu transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_U; break; case 38: // stb transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break; case 39: // stbu transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break; case 40: // lhz transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break; case 41: // lhzu transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break; case 42: // lha transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break; case 43: // lhau transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break; case 44: // sth transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break; case 45: // sthu transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break; } // Calculate effective address uint32 addr = 0; switch (addr_mode) { case MODE_X: case MODE_UX: if (ra == 0) addr = segv_r[rb]; else addr = segv_r[ra] + segv_r[rb]; break; case MODE_NORM: case MODE_U: if (ra == 0) addr = (int32)(int16)imm; else addr = segv_r[ra] + (int32)(int16)imm; break; } // Ignore ROM writes if (transfer_type == TYPE_STORE && addr >= (uint32)ROMBaseHost && addr < (uint32)ROMBaseHost + ROMSize) { // D(bug("WARNING: %s write access to ROM at %p, 68k pc %p\n", transfer_size == SIZE_BYTE ? "Byte" : transfer_size == SIZE_HALFWORD ? "Halfword" : "Word", addr, r->pc)); if (addr_mode == MODE_U || addr_mode == MODE_UX) segv_r[ra] = addr; r->pc += 4; goto rti; } // For all other errors, jump into debugger char str[256]; sprintf(str, "SIGSEGV\n" " pc %08lx lr %08lx ctr %08lx msr %08lx\n" " xer %08lx cr %08lx fpscr %08lx\n" " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n" " r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n" " r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n" " r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n" " r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n" " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n" " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n" " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n", r->pc, r->lr, r->ctr, r->msr, r->xer, r->cr, r->fpscr, r->r0, r->r1, r->r2, r->r3, r->r4, r->r5, r->r6, r->r7, r->r8, r->r9, r->r10, r->r11, r->r12, segv_r[13], segv_r[14], segv_r[15], segv_r[16], segv_r[17], segv_r[18], segv_r[19], segv_r[20], segv_r[21], segv_r[22], segv_r[23], segv_r[24], segv_r[25], segv_r[26], segv_r[27], segv_r[28], segv_r[29], segv_r[30], segv_r[31]); disable_debugger(false); debugger(str); QuitEmulator(); return; rti: // Restore volatile registers r->r0 = segv_r[0]; r->r1 = segv_r[1]; r->r2 = segv_r[2]; r->r3 = segv_r[3]; r->r4 = segv_r[4]; r->r5 = segv_r[5]; r->r6 = segv_r[6]; r->r7 = segv_r[7]; r->r8 = segv_r[8]; r->r9 = segv_r[9]; r->r10 = segv_r[10]; r->r11 = segv_r[11]; r->r12 = segv_r[12]; } #endif