home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 6 / AACD06.ISO / AACD / Emulation / BasiliskII / src / BeOS / main_beos.cpp < prev    next >
C/C++ Source or Header  |  1999-10-19  |  19KB  |  794 lines

  1. /*
  2.  *  main_beos.cpp - Startup code for BeOS
  3.  *
  4.  *  Basilisk II (C) 1997-1999 Christian Bauer
  5.  *
  6.  *  This program is free software; you can redistribute it and/or modify
  7.  *  it under the terms of the GNU General Public License as published by
  8.  *  the Free Software Foundation; either version 2 of the License, or
  9.  *  (at your option) any later version.
  10.  *
  11.  *  This program is distributed in the hope that it will be useful,
  12.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  *  GNU General Public License for more details.
  15.  *
  16.  *  You should have received a copy of the GNU General Public License
  17.  *  along with this program; if not, write to the Free Software
  18.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  */
  20.  
  21. #include <AppKit.h>
  22. #include <InterfaceKit.h>
  23. #include <KernelKit.h>
  24. #include <StorageKit.h>
  25.  
  26. #include <unistd.h>
  27. #include <fcntl.h>
  28. #include <signal.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <time.h>
  32.  
  33. #include "sysdeps.h"
  34. #include "cpu_emulation.h"
  35. #include "xpram.h"
  36. #include "timer.h"
  37. #include "video.h"
  38. #include "rom_patches.h"
  39. #include "prefs.h"
  40. #include "prefs_editor.h"
  41. #include "sys.h"
  42. #include "user_strings.h"
  43. #include "version.h"
  44. #include "main.h"
  45.  
  46. #include "sheep_driver.h"
  47.  
  48. #define DEBUG 0
  49. #include "debug.h"
  50.  
  51.  
  52. // Constants
  53. const char APP_SIGNATURE[] = "application/x-vnd.cebix-BasiliskII";
  54. const char ROM_FILE_NAME[] = "ROM";
  55. const char RAM_AREA_NAME[] = "Macintosh RAM";
  56. const char ROM_AREA_NAME[] = "Macintosh ROM";
  57. const uint32 MSG_START = 'strt';            // Emulator start message
  58. const uint32 ROM_AREA_SIZE = 0x500000;        // Enough to hold PowerMac ROM (for powerrom_cpu)
  59.  
  60. // Prototypes
  61. #if __POWERPC__
  62. static void sigsegv_handler(vregs *r);
  63. #endif
  64.  
  65.  
  66. // Application object
  67. class BasiliskII : public BApplication {
  68. public:
  69.     BasiliskII() : BApplication(APP_SIGNATURE)
  70.     {
  71.         // Find application directory and cwd to it
  72.         app_info the_info;
  73.         GetAppInfo(&the_info);
  74.         BEntry the_file(&the_info.ref);
  75.         BEntry the_dir;
  76.         the_file.GetParent(&the_dir);
  77.         BPath the_path;
  78.         the_dir.GetPath(&the_path);
  79.         chdir(the_path.Path());
  80.  
  81.         // Initialize other variables
  82.         rom_area = ram_area = -1;
  83.         xpram_thread = tick_thread = -1;
  84.         xpram_thread_active = true;
  85.         tick_thread_active = true;
  86.         AllowQuitting = true;
  87.     }
  88.     virtual void ReadyToRun(void);
  89.     virtual void MessageReceived(BMessage *msg);
  90.     void StartEmulator(void);
  91.     virtual bool QuitRequested(void);
  92.     virtual void Quit(void);
  93.  
  94.     thread_id xpram_thread;        // XPRAM watchdog
  95.     thread_id tick_thread;        // 60Hz thread
  96.  
  97.     volatile bool xpram_thread_active;    // Flag for quitting the XPRAM thread
  98.     volatile bool tick_thread_active;    // Flag for quitting the 60Hz thread
  99.  
  100.     bool AllowQuitting;            // Flag: Alt-Q quitting allowed
  101.  
  102. private:
  103.     static status_t emul_func(void *arg);
  104.     static status_t tick_func(void *arg);
  105.     static status_t xpram_func(void *arg);
  106.     static void sigsegv_invoc(int sig, void *arg, vregs *r);
  107.  
  108.     void init_rom(void);
  109.     void load_rom(void);
  110.  
  111.     area_id rom_area;        // ROM area ID
  112.     area_id ram_area;        // RAM area ID
  113.  
  114.     struct sigaction sigsegv_action;    // Data access exception signal (of emulator thread)
  115.  
  116.     // Exceptions
  117.     class area_error {};
  118.     class file_open_error {};
  119.     class file_read_error {};
  120.     class rom_size_error {};
  121. };
  122.  
  123. static BasiliskII *the_app;
  124.  
  125.  
  126. // CPU and FPU type, addressing mode
  127. int CPUType;
  128. bool CPUIs68060;
  129. int FPUType;
  130. bool TwentyFourBitAddressing;
  131.  
  132.  
  133. // Global variables for PowerROM CPU
  134. thread_id emul_thread = -1;            // Emulator thread
  135.  
  136. #if __POWERPC__
  137. int sheep_fd = -1;                    // fd of sheep driver
  138. #endif
  139.  
  140.  
  141. /*
  142.  *  Create application object and start it
  143.  */
  144.  
  145. int main(int argc, char **argv)
  146. {    
  147.     the_app = new BasiliskII();
  148.     the_app->Run();
  149.     delete the_app;
  150.     return 0;
  151. }
  152.  
  153.  
  154. /*
  155.  *  Run application
  156.  */
  157.  
  158. void BasiliskII::ReadyToRun(void)
  159. {
  160.     // Initialize variables
  161.     RAMBaseHost = NULL;
  162.     ROMBaseHost = NULL;
  163.     srand(real_time_clock());
  164.     tzset();
  165.  
  166.     // Print some info
  167.     printf(GetString(STR_ABOUT_TEXT1), VERSION_MAJOR, VERSION_MINOR);
  168.     printf(" %s\n", GetString(STR_ABOUT_TEXT2));
  169.  
  170.     // Delete old areas
  171.     area_id old_ram_area = find_area(RAM_AREA_NAME);
  172.     if (old_ram_area > 0)
  173.         delete_area(old_ram_area);
  174.     area_id old_rom_area = find_area(ROM_AREA_NAME);
  175.     if (old_rom_area > 0)
  176.         delete_area(old_rom_area);
  177.  
  178.     // Read preferences
  179.     PrefsInit();
  180.  
  181.     // Init system routines
  182.     SysInit();
  183.  
  184.     // Show preferences editor (or start emulator directly)
  185.     if (!PrefsFindBool("nogui"))
  186.         PrefsEditor();
  187.     else
  188.         PostMessage(MSG_START);
  189. }
  190.  
  191.  
  192. /*
  193.  *  Message received
  194.  */
  195.  
  196. void BasiliskII::MessageReceived(BMessage *msg)
  197. {
  198.     switch (msg->what) {
  199.         case MSG_START:
  200.             StartEmulator();
  201.             break;
  202.         default:
  203.             BApplication::MessageReceived(msg);
  204.     }
  205. }
  206.  
  207.  
  208. /*
  209.  *  Start emulator
  210.  */
  211.  
  212. void BasiliskII::StartEmulator(void)
  213. {
  214.     char str[256];
  215.  
  216. #if REAL_ADDRESSING
  217.     // Open memory mess driver and remap low memory
  218.     sheep_fd = open("/dev/sheep", 0);
  219.     if (sheep_fd < 0) {
  220.         sprintf(str, GetString(STR_NO_SHEEP_DRIVER_ERR), strerror(sheep_fd), sheep_fd);
  221.         ErrorAlert(str);
  222.         PostMessage(B_QUIT_REQUESTED);
  223.         return;
  224.     }
  225.     status_t res = ioctl(sheep_fd, SHEEP_UP);
  226.     if (res < 0) {
  227.         sprintf(str, GetString(STR_SHEEP_UP_ERR), strerror(res), res);
  228.         ErrorAlert(str);
  229.         PostMessage(B_QUIT_REQUESTED);
  230.         return;
  231.     }
  232. #endif
  233.  
  234.     // Create area for Mac RAM
  235.     RAMSize = PrefsFindInt32("ramsize") & 0xfff00000;    // Round down to 1MB boundary
  236.     if (RAMSize < 1024*1024) {
  237.         WarningAlert(GetString(STR_SMALL_RAM_WARN));
  238.         RAMSize = 1024*1024;
  239.     }
  240.  
  241.     RAMBaseHost = (uint8 *)0x10000000;
  242.     ram_area = create_area(RAM_AREA_NAME, (void **)&RAMBaseHost, B_BASE_ADDRESS, RAMSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
  243.     if (ram_area < 0) {
  244.         ErrorAlert(GetString(STR_NO_RAM_AREA_ERR));
  245.         PostMessage(B_QUIT_REQUESTED);
  246.         return;
  247.     }
  248.     D(bug("RAM area %ld at %p\n", ram_area, RAMBaseHost));
  249.  
  250.     // Create area and load Mac ROM
  251.     try {
  252.         init_rom();
  253.     } catch (area_error) {
  254.         ErrorAlert(GetString(STR_NO_ROM_AREA_ERR));
  255.         PostMessage(B_QUIT_REQUESTED);
  256.         return;
  257.     } catch (file_open_error) {
  258.         ErrorAlert(GetString(STR_NO_ROM_FILE_ERR));
  259.         PostMessage(B_QUIT_REQUESTED);
  260.         return;
  261.     } catch (file_read_error) {
  262.         ErrorAlert(GetString(STR_ROM_FILE_READ_ERR));
  263.         PostMessage(B_QUIT_REQUESTED);
  264.         return;
  265.     } catch (rom_size_error) {
  266.         ErrorAlert(GetString(STR_ROM_SIZE_ERR));
  267.         PostMessage(B_QUIT_REQUESTED);
  268.         return;
  269.     }
  270.  
  271.     // Initialize everything
  272.     if (!InitAll()) {
  273.         PostMessage(B_QUIT_REQUESTED);
  274.         return;
  275.     }
  276.  
  277.     // Write protect ROM
  278.     set_area_protection(rom_area, B_READ_AREA);
  279.  
  280.     // Disallow quitting with Alt-Q from now on
  281.     AllowQuitting = false;
  282.  
  283.     // Start XPRAM watchdog thread
  284.     xpram_thread = spawn_thread(xpram_func, "XPRAM Watchdog", B_LOW_PRIORITY, this);
  285.     resume_thread(xpram_thread);
  286.  
  287.     // Start 60Hz interrupt
  288.     tick_thread = spawn_thread(tick_func, "60Hz", B_REAL_TIME_PRIORITY, this);
  289.     resume_thread(tick_thread);
  290.  
  291.     // Start emulator thread
  292.     emul_thread = spawn_thread(emul_func, "MacOS", B_NORMAL_PRIORITY, this);
  293.     resume_thread(emul_thread);
  294. }
  295.  
  296.  
  297. /*
  298.  *  Quit emulator
  299.  */
  300.  
  301. void QuitEmulator(void)
  302. {
  303.     the_app->AllowQuitting = true;
  304.     be_app->PostMessage(B_QUIT_REQUESTED);
  305.     exit_thread(0);
  306. }
  307.  
  308. bool BasiliskII::QuitRequested(void)
  309. {
  310.     if (AllowQuitting)
  311.         return BApplication::QuitRequested();
  312.     else
  313.         return false;
  314. }
  315.  
  316. void BasiliskII::Quit(void)
  317. {
  318.     status_t l;
  319.  
  320.     // Stop 60Hz interrupt
  321.     if (tick_thread > 0) {
  322.         tick_thread_active = false;
  323.         wait_for_thread(tick_thread, &l);
  324.     }
  325.  
  326.     // Wait for emulator thread to finish
  327.     if (emul_thread > 0)
  328.         wait_for_thread(emul_thread, &l);
  329.  
  330.     // Exit 680x0 emulation
  331.     Exit680x0();
  332.  
  333.     // Stop XPRAM watchdog thread
  334.     if (xpram_thread > 0) {
  335.         xpram_thread_active = false;
  336.         suspend_thread(xpram_thread);    // Wake thread up from snooze()
  337.         snooze(1000);
  338.         resume_thread(xpram_thread);
  339.         wait_for_thread(xpram_thread, &l);
  340.     }
  341.  
  342.     // Deinitialize everything
  343.     ExitAll();
  344.  
  345.     // Delete ROM area
  346.     if (rom_area >= 0)
  347.         delete_area(rom_area);
  348.  
  349.     // Delete RAM area
  350.     if (ram_area >= 0)
  351.         delete_area(ram_area);
  352.  
  353. #if REAL_ADDRESSING
  354.     // Unmap low memory and close memory mess driver
  355.     if (sheep_fd >= 0) {
  356.         ioctl(sheep_fd, SHEEP_DOWN);
  357.         close(sheep_fd);
  358.     }
  359. #endif
  360.  
  361.     // Exit system routines
  362.     SysExit();
  363.  
  364.     // Exit preferences
  365.     PrefsExit();
  366.  
  367.     BApplication::Quit();
  368. }
  369.  
  370.  
  371. /*
  372.  *  Create area for ROM (sets rom_area) and load ROM file
  373.  *
  374.  *  area_error     : Cannot create area
  375.  *  file_open_error: Cannot open ROM file
  376.  *  file_read_error: Cannot read ROM file
  377.  */
  378.  
  379. void BasiliskII::init_rom(void)
  380. {
  381.     // Create area for ROM
  382.     ROMBaseHost = (uint8 *)0x40800000;
  383.     rom_area = create_area(ROM_AREA_NAME, (void **)&ROMBaseHost, B_BASE_ADDRESS, ROM_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
  384.     if (rom_area < 0)
  385.         throw area_error();
  386.     D(bug("ROM area %ld at %p\n", rom_area, ROMBaseHost));
  387.  
  388.     // Load ROM
  389.     load_rom();
  390. }
  391.  
  392.  
  393. /*
  394.  *  Load ROM file
  395.  *
  396.  *  file_open_error: Cannot open ROM file
  397.  *  file_read_error: Cannot read ROM file
  398.  */
  399.  
  400. void BasiliskII::load_rom(void)
  401. {
  402.     // Get rom file path from preferences
  403.     const char *rom_path = PrefsFindString("rom");
  404.  
  405.     // Try to open ROM file
  406.     BFile file(rom_path ? rom_path : ROM_FILE_NAME, B_READ_ONLY);
  407.     if (file.InitCheck() != B_NO_ERROR)
  408.         throw file_open_error();
  409.  
  410.     printf(GetString(STR_READING_ROM_FILE));
  411.  
  412.     // Is the ROM size correct?
  413.     off_t rom_size = 0;
  414.     file.GetSize(&rom_size);
  415.     if (rom_size != 64*1024 && rom_size != 128*1024 && rom_size != 256*1024 && rom_size != 512*1024 && rom_size != 1024*1024)
  416.         throw rom_size_error();
  417.  
  418.     uint8 *rom = new uint8[rom_size];    // Reading directly into the area doesn't work
  419.     ssize_t actual = file.Read((void *)rom, rom_size);
  420.     if (actual == rom_size)
  421.         memcpy(ROMBaseHost, rom, rom_size);
  422.     delete[] rom;
  423.     if (actual != rom_size)
  424.         throw file_read_error();
  425.     ROMSize = rom_size;
  426. }
  427.  
  428.  
  429. /*
  430.  *  Emulator thread function
  431.  */
  432.  
  433. status_t BasiliskII::emul_func(void *arg)
  434. {
  435.     BasiliskII *obj = (BasiliskII *)arg;
  436.  
  437. #if __POWERPC__
  438.     // Install data access signal handler
  439.     sigemptyset(&obj->sigsegv_action.sa_mask);
  440.     obj->sigsegv_action.sa_handler = (__signal_func_ptr)(obj->sigsegv_invoc);
  441.     obj->sigsegv_action.sa_flags = 0;
  442.     obj->sigsegv_action.sa_userdata = arg;
  443.     sigaction(SIGSEGV, &obj->sigsegv_action, NULL);
  444. #endif
  445.  
  446.     // Exceptions will send signals
  447.     disable_debugger(true);
  448.  
  449.     // Start 68k and jump to ROM boot routine
  450.     Start680x0();
  451.  
  452.     // Quit program
  453.     obj->AllowQuitting = true;
  454.     be_app->PostMessage(B_QUIT_REQUESTED);
  455.     return 0;
  456. }
  457.  
  458.  
  459. /*
  460.  *  Code was patched, flush caches if neccessary (i.e. when using a real 680x0
  461.  *  or a dynamically recompiling emulator)
  462.  */
  463.  
  464. void FlushCodeCache(void *start, uint32 size)
  465. {
  466. }
  467.  
  468.  
  469. /*
  470.  *  Interrupt flags (must be handled atomically!)
  471.  */
  472.  
  473. uint32 InterruptFlags = 0;
  474.  
  475. void SetInterruptFlag(uint32 flag)
  476. {
  477.     atomic_or((int32 *)&InterruptFlags, flag);
  478. }
  479.  
  480. void ClearInterruptFlag(uint32 flag)
  481. {
  482.     atomic_and((int32 *)&InterruptFlags, ~flag);
  483. }
  484.  
  485.  
  486. /*
  487.  *  60Hz thread (really 60.15Hz)
  488.  */
  489.  
  490. status_t BasiliskII::tick_func(void *arg)
  491. {
  492.     BasiliskII *obj = (BasiliskII *)arg;
  493.     int tick_counter = 0;
  494.     bigtime_t current = system_time();
  495.  
  496.     while (obj->tick_thread_active) {
  497.  
  498.         // Wait
  499.         current += 16625;
  500.         snooze_until(current, B_SYSTEM_TIMEBASE);
  501.  
  502.         // Pseudo Mac 1Hz interrupt, update local time
  503.         if (++tick_counter > 60) {
  504.             tick_counter = 0;
  505.             WriteMacInt32(0x20c, TimerDateTime());
  506.         }
  507.  
  508.         // Trigger 60Hz interrupt
  509.         SetInterruptFlag(INTFLAG_60HZ);
  510.         TriggerInterrupt();
  511.     }
  512.     return 0;
  513. }
  514.  
  515.  
  516. /*
  517.  *  XPRAM watchdog thread (saves XPRAM every minute)
  518.  */
  519.  
  520. status_t BasiliskII::xpram_func(void *arg)
  521. {
  522.     uint8 last_xpram[256];
  523.     memcpy(last_xpram, XPRAM, 256);
  524.  
  525.     while (((BasiliskII *)arg)->xpram_thread_active) {
  526.         snooze(60*1000000);
  527.         if (memcmp(last_xpram, XPRAM, 256)) {
  528.             memcpy(last_xpram, XPRAM, 256);
  529.             SaveXPRAM();
  530.         }
  531.     }
  532.     return 0;
  533. }
  534.  
  535.  
  536. /*
  537.  *  Display error alert
  538.  */
  539.  
  540. void ErrorAlert(const char *text)
  541. {
  542.     if (PrefsFindBool("nogui")) {
  543.         printf(GetString(STR_SHELL_ERROR_PREFIX), text);
  544.         return;
  545.     }
  546.     VideoQuitFullScreen();
  547.     char str[256];
  548.     sprintf(str, GetString(STR_GUI_ERROR_PREFIX), text);
  549.     BAlert *alert = new BAlert(GetString(STR_ERROR_ALERT_TITLE), str, GetString(STR_QUIT_BUTTON), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
  550.     alert->Go();
  551. }
  552.  
  553.  
  554. /*
  555.  *  Display warning alert
  556.  */
  557.  
  558. void WarningAlert(const char *text)
  559. {
  560.     if (PrefsFindBool("nogui")) {
  561.         printf(GetString(STR_SHELL_WARNING_PREFIX), text);
  562.         return;
  563.     }
  564.     char str[256];
  565.     sprintf(str, GetString(STR_GUI_WARNING_PREFIX), text);
  566.     BAlert *alert = new BAlert(GetString(STR_WARNING_ALERT_TITLE), str, GetString(STR_OK_BUTTON), NULL, NULL, B_WIDTH_AS_USUAL, B_INFO_ALERT);
  567.     alert->Go();
  568. }
  569.  
  570.  
  571. /*
  572.  *  Display choice alert
  573.  */
  574.  
  575. bool ChoiceAlert(const char *text, const char *pos, const char *neg)
  576. {
  577.     char str[256];
  578.     sprintf(str, GetString(STR_GUI_WARNING_PREFIX), text);
  579.     BAlert *alert = new BAlert(GetString(STR_WARNING_ALERT_TITLE), str, pos, neg, NULL, B_WIDTH_AS_USUAL, B_INFO_ALERT);
  580.     return alert->Go() == 0;
  581. }
  582.  
  583.  
  584. /*
  585.  *  SEGV handler
  586.  */
  587.  
  588. #if __POWERPC__
  589. static uint32 segv_r[32];
  590.  
  591. asm void BasiliskII::sigsegv_invoc(register int sig, register void *arg, register vregs *r)
  592. {
  593.     mflr    r0
  594.     stw        r0,8(r1)
  595.     stwu    r1,-56(r1)
  596.  
  597.     lwz        r3,segv_r(r2)
  598.     stmw    r13,13*4(r3)
  599.  
  600.     mr        r3,r5
  601.     bl        sigsegv_handler
  602.  
  603.     lwz        r3,segv_r(r2)
  604.     lmw        r13,13*4(r3)
  605.  
  606.     lwz        r0,56+8(r1)
  607.     mtlr    r0
  608.     addi    r1,r1,56
  609.     blr
  610. }
  611.  
  612. static void sigsegv_handler(vregs *r)
  613. {
  614.     // Fetch volatile registers
  615.     segv_r[0] = r->r0;
  616.     segv_r[1] = r->r1;
  617.     segv_r[2] = r->r2;
  618.     segv_r[3] = r->r3;
  619.     segv_r[4] = r->r4;
  620.     segv_r[5] = r->r5;
  621.     segv_r[6] = r->r6;
  622.     segv_r[7] = r->r7;
  623.     segv_r[8] = r->r8;
  624.     segv_r[9] = r->r9;
  625.     segv_r[10] = r->r10;
  626.     segv_r[11] = r->r11;
  627.     segv_r[12] = r->r12;
  628.  
  629.     // Get opcode and divide into fields
  630.     uint32 opcode = *(uint32 *)r->pc;
  631.     uint32 primop = opcode >> 26;
  632.     uint32 exop = (opcode >> 1) & 0x3ff;
  633.     uint32 ra = (opcode >> 16) & 0x1f;
  634.     uint32 rb = (opcode >> 11) & 0x1f;
  635.     uint32 rd = (opcode >> 21) & 0x1f;
  636.     uint32 imm = opcode & 0xffff;
  637.  
  638.     // Analyze opcode
  639.     enum {
  640.         TYPE_UNKNOWN,
  641.         TYPE_LOAD,
  642.         TYPE_STORE
  643.     } transfer_type = TYPE_UNKNOWN;
  644.     enum {
  645.         SIZE_UNKNOWN,
  646.         SIZE_BYTE,
  647.         SIZE_HALFWORD,
  648.         SIZE_WORD
  649.     } transfer_size = SIZE_UNKNOWN;
  650.     enum {
  651.         MODE_UNKNOWN,
  652.         MODE_NORM,
  653.         MODE_U,
  654.         MODE_X,
  655.         MODE_UX
  656.     } addr_mode = MODE_UNKNOWN;
  657.     switch (primop) {
  658.         case 31:
  659.             switch (exop) {
  660.                 case 23:    // lwzx
  661.                     transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_X; break;
  662.                 case 55:    // lwzux
  663.                     transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break;
  664.                 case 87:    // lbzx
  665.                     transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break;
  666.                 case 119:    // lbzux
  667.                     transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break;
  668.                 case 151:    // stwx
  669.                     transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_X; break;
  670.                 case 183:    // stwux
  671.                     transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break;
  672.                 case 215:    // stbx
  673.                     transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break;
  674.                 case 247:    // stbux
  675.                     transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break;
  676.                 case 279:    // lhzx
  677.                     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
  678.                 case 311:    // lhzux
  679.                     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
  680.                 case 343:    // lhax
  681.                     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
  682.                 case 375:    // lhaux
  683.                     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
  684.                 case 407:    // sthx
  685.                     transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
  686.                 case 439:    // sthux
  687.                     transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
  688.             }
  689.             break;
  690.  
  691.         case 32:    // lwz
  692.             transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break;
  693.         case 33:    // lwzu
  694.             transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_U; break;
  695.         case 34:    // lbz
  696.             transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break;
  697.         case 35:    // lbzu
  698.             transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break;
  699.         case 36:    // stw
  700.             transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break;
  701.         case 37:    // stwu
  702.             transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_U; break;
  703.         case 38:    // stb
  704.             transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break;
  705.         case 39:    // stbu
  706.             transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break;
  707.         case 40:    // lhz
  708.             transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
  709.         case 41:    // lhzu
  710.             transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
  711.         case 42:    // lha
  712.             transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
  713.         case 43:    // lhau
  714.             transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
  715.         case 44:    // sth
  716.             transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
  717.         case 45:    // sthu
  718.             transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
  719.     }
  720.  
  721.     // Calculate effective address
  722.     uint32 addr = 0;
  723.     switch (addr_mode) {
  724.         case MODE_X:
  725.         case MODE_UX:
  726.             if (ra == 0)
  727.                 addr = segv_r[rb];
  728.             else
  729.                 addr = segv_r[ra] + segv_r[rb];
  730.             break;
  731.         case MODE_NORM:
  732.         case MODE_U:
  733.             if (ra == 0)
  734.                 addr = (int32)(int16)imm;
  735.             else
  736.                 addr = segv_r[ra] + (int32)(int16)imm;
  737.             break;
  738.     }
  739.  
  740.     // Ignore ROM writes
  741.     if (transfer_type == TYPE_STORE && addr >= (uint32)ROMBaseHost && addr < (uint32)ROMBaseHost + ROMSize) {
  742. //        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));
  743.         if (addr_mode == MODE_U || addr_mode == MODE_UX)
  744.             segv_r[ra] = addr;
  745.         r->pc += 4;
  746.         goto rti;
  747.     }
  748.  
  749.     // For all other errors, jump into debugger
  750.     char str[256];
  751.     sprintf(str, "SIGSEGV\n"
  752.         "   pc %08lx     lr %08lx    ctr %08lx    msr %08lx\n"
  753.         "  xer %08lx     cr %08lx  fpscr %08lx\n"
  754.         "   r0 %08lx     r1 %08lx     r2 %08lx     r3 %08lx\n"
  755.         "   r4 %08lx     r5 %08lx     r6 %08lx     r7 %08lx\n"
  756.         "   r8 %08lx     r9 %08lx    r10 %08lx    r11 %08lx\n"
  757.         "  r12 %08lx    r13 %08lx    r14 %08lx    r15 %08lx\n"
  758.         "  r16 %08lx    r17 %08lx    r18 %08lx    r19 %08lx\n"
  759.         "  r20 %08lx    r21 %08lx    r22 %08lx    r23 %08lx\n"
  760.         "  r24 %08lx    r25 %08lx    r26 %08lx    r27 %08lx\n"
  761.         "  r28 %08lx    r29 %08lx    r30 %08lx    r31 %08lx\n",
  762.         r->pc, r->lr, r->ctr, r->msr,
  763.         r->xer, r->cr, r->fpscr,
  764.         r->r0, r->r1, r->r2, r->r3,
  765.         r->r4, r->r5, r->r6, r->r7,
  766.         r->r8, r->r9, r->r10, r->r11,
  767.         r->r12, segv_r[13], segv_r[14], segv_r[15],
  768.         segv_r[16], segv_r[17], segv_r[18], segv_r[19],
  769.         segv_r[20], segv_r[21], segv_r[22], segv_r[23],
  770.         segv_r[24], segv_r[25], segv_r[26], segv_r[27],
  771.         segv_r[28], segv_r[29], segv_r[30], segv_r[31]);
  772.     disable_debugger(false);
  773.     debugger(str);
  774.     QuitEmulator();
  775.     return;
  776.  
  777. rti:
  778.     // Restore volatile registers
  779.     r->r0 = segv_r[0];
  780.     r->r1 = segv_r[1];
  781.     r->r2 = segv_r[2];
  782.     r->r3 = segv_r[3];
  783.     r->r4 = segv_r[4];
  784.     r->r5 = segv_r[5];
  785.     r->r6 = segv_r[6];
  786.     r->r7 = segv_r[7];
  787.     r->r8 = segv_r[8];
  788.     r->r9 = segv_r[9];
  789.     r->r10 = segv_r[10];
  790.     r->r11 = segv_r[11];
  791.     r->r12 = segv_r[12];
  792. }
  793. #endif
  794.