ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/BeOS/main_beos.cpp
Revision: 1.14
Committed: 2005-01-30T21:42:13Z (19 years, 7 months ago) by gbeauche
Branch: MAIN
CVS Tags: nigel-build-19, nigel-build-17
Changes since 1.13: +1 -1 lines
Log Message:
Happy New Year!

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * main_beos.cpp - Startup code for BeOS
3     *
4 gbeauche 1.14 * Basilisk II (C) 1997-2005 Christian Bauer
5 cebix 1.1 *
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 cebix 1.8 int argc = 0;
180     char **argv = NULL;
181     PrefsInit(argc, argv);
182 cebix 1.1
183     // Init system routines
184     SysInit();
185    
186     // Show preferences editor (or start emulator directly)
187     if (!PrefsFindBool("nogui"))
188     PrefsEditor();
189     else
190     PostMessage(MSG_START);
191     }
192    
193    
194     /*
195     * Message received
196     */
197    
198     void BasiliskII::MessageReceived(BMessage *msg)
199     {
200     switch (msg->what) {
201     case MSG_START:
202     StartEmulator();
203     break;
204     default:
205     BApplication::MessageReceived(msg);
206     }
207     }
208    
209    
210     /*
211     * Start emulator
212     */
213    
214     void BasiliskII::StartEmulator(void)
215     {
216     char str[256];
217    
218     #if REAL_ADDRESSING
219 cebix 1.5 // Open sheep driver and remap low memory
220 cebix 1.1 sheep_fd = open("/dev/sheep", 0);
221     if (sheep_fd < 0) {
222     sprintf(str, GetString(STR_NO_SHEEP_DRIVER_ERR), strerror(sheep_fd), sheep_fd);
223     ErrorAlert(str);
224     PostMessage(B_QUIT_REQUESTED);
225     return;
226     }
227     status_t res = ioctl(sheep_fd, SHEEP_UP);
228     if (res < 0) {
229     sprintf(str, GetString(STR_SHEEP_UP_ERR), strerror(res), res);
230     ErrorAlert(str);
231     PostMessage(B_QUIT_REQUESTED);
232     return;
233     }
234     #endif
235    
236     // Create area for Mac RAM
237     RAMSize = PrefsFindInt32("ramsize") & 0xfff00000; // Round down to 1MB boundary
238     if (RAMSize < 1024*1024) {
239     WarningAlert(GetString(STR_SMALL_RAM_WARN));
240     RAMSize = 1024*1024;
241     }
242    
243     RAMBaseHost = (uint8 *)0x10000000;
244     ram_area = create_area(RAM_AREA_NAME, (void **)&RAMBaseHost, B_BASE_ADDRESS, RAMSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
245     if (ram_area < 0) {
246 cebix 1.9 ErrorAlert(STR_NO_RAM_AREA_ERR);
247 cebix 1.1 PostMessage(B_QUIT_REQUESTED);
248     return;
249     }
250     D(bug("RAM area %ld at %p\n", ram_area, RAMBaseHost));
251    
252     // Create area and load Mac ROM
253     try {
254     init_rom();
255     } catch (area_error) {
256 cebix 1.9 ErrorAlert(STR_NO_ROM_AREA_ERR);
257 cebix 1.1 PostMessage(B_QUIT_REQUESTED);
258     return;
259     } catch (file_open_error) {
260 cebix 1.9 ErrorAlert(STR_NO_ROM_FILE_ERR);
261 cebix 1.1 PostMessage(B_QUIT_REQUESTED);
262     return;
263     } catch (file_read_error) {
264 cebix 1.9 ErrorAlert(STR_ROM_FILE_READ_ERR);
265 cebix 1.1 PostMessage(B_QUIT_REQUESTED);
266     return;
267     } catch (rom_size_error) {
268 cebix 1.9 ErrorAlert(STR_ROM_SIZE_ERR);
269 cebix 1.1 PostMessage(B_QUIT_REQUESTED);
270     return;
271     }
272    
273 cebix 1.2 // Initialize everything
274     if (!InitAll()) {
275 cebix 1.1 PostMessage(B_QUIT_REQUESTED);
276     return;
277     }
278    
279 cebix 1.2 // Write protect ROM
280     set_area_protection(rom_area, B_READ_AREA);
281 cebix 1.1
282 cebix 1.2 // Disallow quitting with Alt-Q from now on
283     AllowQuitting = false;
284 cebix 1.1
285     // Start XPRAM watchdog thread
286     xpram_thread = spawn_thread(xpram_func, "XPRAM Watchdog", B_LOW_PRIORITY, this);
287     resume_thread(xpram_thread);
288    
289     // Start 60Hz interrupt
290     tick_thread = spawn_thread(tick_func, "60Hz", B_REAL_TIME_PRIORITY, this);
291     resume_thread(tick_thread);
292    
293     // Start emulator thread
294     emul_thread = spawn_thread(emul_func, "MacOS", B_NORMAL_PRIORITY, this);
295     resume_thread(emul_thread);
296     }
297    
298    
299     /*
300     * Quit emulator
301     */
302    
303     void QuitEmulator(void)
304     {
305     the_app->AllowQuitting = true;
306     be_app->PostMessage(B_QUIT_REQUESTED);
307     exit_thread(0);
308     }
309    
310     bool BasiliskII::QuitRequested(void)
311     {
312     if (AllowQuitting)
313     return BApplication::QuitRequested();
314     else
315     return false;
316     }
317    
318     void BasiliskII::Quit(void)
319     {
320     status_t l;
321    
322     // Stop 60Hz interrupt
323     if (tick_thread > 0) {
324     tick_thread_active = false;
325     wait_for_thread(tick_thread, &l);
326     }
327    
328     // Wait for emulator thread to finish
329     if (emul_thread > 0)
330     wait_for_thread(emul_thread, &l);
331    
332     // Exit 680x0 emulation
333     Exit680x0();
334    
335     // Stop XPRAM watchdog thread
336     if (xpram_thread > 0) {
337     xpram_thread_active = false;
338     suspend_thread(xpram_thread); // Wake thread up from snooze()
339     snooze(1000);
340     resume_thread(xpram_thread);
341     wait_for_thread(xpram_thread, &l);
342     }
343    
344 cebix 1.2 // Deinitialize everything
345     ExitAll();
346 cebix 1.1
347     // Delete ROM area
348     if (rom_area >= 0)
349     delete_area(rom_area);
350    
351     // Delete RAM area
352     if (ram_area >= 0)
353     delete_area(ram_area);
354    
355     #if REAL_ADDRESSING
356     // Unmap low memory and close memory mess driver
357     if (sheep_fd >= 0) {
358     ioctl(sheep_fd, SHEEP_DOWN);
359     close(sheep_fd);
360     }
361     #endif
362    
363     // Exit system routines
364     SysExit();
365    
366     // Exit preferences
367     PrefsExit();
368    
369     BApplication::Quit();
370     }
371    
372    
373     /*
374     * Create area for ROM (sets rom_area) and load ROM file
375     *
376     * area_error : Cannot create area
377     * file_open_error: Cannot open ROM file
378     * file_read_error: Cannot read ROM file
379     */
380    
381     void BasiliskII::init_rom(void)
382     {
383     // Create area for ROM
384     ROMBaseHost = (uint8 *)0x40800000;
385     rom_area = create_area(ROM_AREA_NAME, (void **)&ROMBaseHost, B_BASE_ADDRESS, ROM_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
386     if (rom_area < 0)
387     throw area_error();
388     D(bug("ROM area %ld at %p\n", rom_area, ROMBaseHost));
389    
390     // Load ROM
391     load_rom();
392     }
393    
394    
395     /*
396     * Load ROM file
397     *
398     * file_open_error: Cannot open ROM file
399     * file_read_error: Cannot read ROM file
400     */
401    
402     void BasiliskII::load_rom(void)
403     {
404     // Get rom file path from preferences
405     const char *rom_path = PrefsFindString("rom");
406    
407     // Try to open ROM file
408     BFile file(rom_path ? rom_path : ROM_FILE_NAME, B_READ_ONLY);
409     if (file.InitCheck() != B_NO_ERROR)
410     throw file_open_error();
411    
412     printf(GetString(STR_READING_ROM_FILE));
413    
414     // Is the ROM size correct?
415     off_t rom_size = 0;
416     file.GetSize(&rom_size);
417     if (rom_size != 64*1024 && rom_size != 128*1024 && rom_size != 256*1024 && rom_size != 512*1024 && rom_size != 1024*1024)
418     throw rom_size_error();
419    
420     uint8 *rom = new uint8[rom_size]; // Reading directly into the area doesn't work
421     ssize_t actual = file.Read((void *)rom, rom_size);
422     if (actual == rom_size)
423     memcpy(ROMBaseHost, rom, rom_size);
424     delete[] rom;
425     if (actual != rom_size)
426     throw file_read_error();
427     ROMSize = rom_size;
428     }
429    
430    
431     /*
432     * Emulator thread function
433     */
434    
435     status_t BasiliskII::emul_func(void *arg)
436     {
437     BasiliskII *obj = (BasiliskII *)arg;
438    
439     #if __POWERPC__
440     // Install data access signal handler
441     sigemptyset(&obj->sigsegv_action.sa_mask);
442     obj->sigsegv_action.sa_handler = (__signal_func_ptr)(obj->sigsegv_invoc);
443     obj->sigsegv_action.sa_flags = 0;
444     obj->sigsegv_action.sa_userdata = arg;
445     sigaction(SIGSEGV, &obj->sigsegv_action, NULL);
446     #endif
447    
448     // Exceptions will send signals
449     disable_debugger(true);
450    
451     // Start 68k and jump to ROM boot routine
452     Start680x0();
453    
454     // Quit program
455     obj->AllowQuitting = true;
456     be_app->PostMessage(B_QUIT_REQUESTED);
457     return 0;
458     }
459    
460    
461     /*
462     * Code was patched, flush caches if neccessary (i.e. when using a real 680x0
463     * or a dynamically recompiling emulator)
464     */
465    
466     void FlushCodeCache(void *start, uint32 size)
467     {
468     }
469    
470    
471     /*
472 cebix 1.10 * Mutexes
473     */
474    
475     struct B2_mutex {
476     int dummy; //!!
477     };
478    
479     B2_mutex *B2_create_mutex(void)
480     {
481     return new B2_mutex;
482     }
483    
484     void B2_lock_mutex(B2_mutex *mutex)
485     {
486     }
487    
488     void B2_unlock_mutex(B2_mutex *mutex)
489     {
490     }
491    
492     void B2_delete_mutex(B2_mutex *mutex)
493     {
494     delete mutex;
495     }
496    
497    
498     /*
499 cebix 1.1 * Interrupt flags (must be handled atomically!)
500     */
501    
502     uint32 InterruptFlags = 0;
503    
504     void SetInterruptFlag(uint32 flag)
505     {
506     atomic_or((int32 *)&InterruptFlags, flag);
507     }
508    
509     void ClearInterruptFlag(uint32 flag)
510     {
511     atomic_and((int32 *)&InterruptFlags, ~flag);
512     }
513    
514    
515     /*
516     * 60Hz thread (really 60.15Hz)
517     */
518    
519     status_t BasiliskII::tick_func(void *arg)
520     {
521     BasiliskII *obj = (BasiliskII *)arg;
522     int tick_counter = 0;
523     bigtime_t current = system_time();
524    
525     while (obj->tick_thread_active) {
526    
527     // Wait
528     current += 16625;
529     snooze_until(current, B_SYSTEM_TIMEBASE);
530    
531     // Pseudo Mac 1Hz interrupt, update local time
532     if (++tick_counter > 60) {
533     tick_counter = 0;
534     WriteMacInt32(0x20c, TimerDateTime());
535 cebix 1.4 SetInterruptFlag(INTFLAG_1HZ);
536     TriggerInterrupt();
537 cebix 1.1 }
538    
539     // Trigger 60Hz interrupt
540     SetInterruptFlag(INTFLAG_60HZ);
541     TriggerInterrupt();
542     }
543     return 0;
544     }
545    
546    
547     /*
548     * XPRAM watchdog thread (saves XPRAM every minute)
549     */
550    
551     status_t BasiliskII::xpram_func(void *arg)
552     {
553 cebix 1.11 uint8 last_xpram[XPRAM_SIZE];
554     memcpy(last_xpram, XPRAM, XPRAM_SIZE);
555 cebix 1.1
556     while (((BasiliskII *)arg)->xpram_thread_active) {
557     snooze(60*1000000);
558 cebix 1.11 if (memcmp(last_xpram, XPRAM, XPRAM_SIZE)) {
559     memcpy(last_xpram, XPRAM, XPRAM_SIZE);
560 cebix 1.1 SaveXPRAM();
561     }
562     }
563     return 0;
564     }
565    
566    
567     /*
568     * Display error alert
569     */
570    
571     void ErrorAlert(const char *text)
572     {
573     if (PrefsFindBool("nogui")) {
574     printf(GetString(STR_SHELL_ERROR_PREFIX), text);
575     return;
576     }
577     VideoQuitFullScreen();
578     char str[256];
579     sprintf(str, GetString(STR_GUI_ERROR_PREFIX), text);
580     BAlert *alert = new BAlert(GetString(STR_ERROR_ALERT_TITLE), str, GetString(STR_QUIT_BUTTON), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
581     alert->Go();
582     }
583    
584    
585     /*
586     * Display warning alert
587     */
588    
589     void WarningAlert(const char *text)
590     {
591     if (PrefsFindBool("nogui")) {
592     printf(GetString(STR_SHELL_WARNING_PREFIX), text);
593     return;
594     }
595     char str[256];
596     sprintf(str, GetString(STR_GUI_WARNING_PREFIX), text);
597     BAlert *alert = new BAlert(GetString(STR_WARNING_ALERT_TITLE), str, GetString(STR_OK_BUTTON), NULL, NULL, B_WIDTH_AS_USUAL, B_INFO_ALERT);
598     alert->Go();
599     }
600    
601    
602     /*
603     * Display choice alert
604     */
605    
606     bool ChoiceAlert(const char *text, const char *pos, const char *neg)
607     {
608     char str[256];
609     sprintf(str, GetString(STR_GUI_WARNING_PREFIX), text);
610     BAlert *alert = new BAlert(GetString(STR_WARNING_ALERT_TITLE), str, pos, neg, NULL, B_WIDTH_AS_USUAL, B_INFO_ALERT);
611     return alert->Go() == 0;
612     }
613    
614    
615     /*
616     * SEGV handler
617     */
618    
619     #if __POWERPC__
620     static uint32 segv_r[32];
621    
622     asm void BasiliskII::sigsegv_invoc(register int sig, register void *arg, register vregs *r)
623     {
624     mflr r0
625     stw r0,8(r1)
626     stwu r1,-56(r1)
627    
628     lwz r3,segv_r(r2)
629     stmw r13,13*4(r3)
630    
631     mr r3,r5
632     bl sigsegv_handler
633    
634     lwz r3,segv_r(r2)
635     lmw r13,13*4(r3)
636    
637     lwz r0,56+8(r1)
638     mtlr r0
639     addi r1,r1,56
640     blr
641     }
642    
643     static void sigsegv_handler(vregs *r)
644     {
645     // Fetch volatile registers
646     segv_r[0] = r->r0;
647     segv_r[1] = r->r1;
648     segv_r[2] = r->r2;
649     segv_r[3] = r->r3;
650     segv_r[4] = r->r4;
651     segv_r[5] = r->r5;
652     segv_r[6] = r->r6;
653     segv_r[7] = r->r7;
654     segv_r[8] = r->r8;
655     segv_r[9] = r->r9;
656     segv_r[10] = r->r10;
657     segv_r[11] = r->r11;
658     segv_r[12] = r->r12;
659    
660     // Get opcode and divide into fields
661     uint32 opcode = *(uint32 *)r->pc;
662     uint32 primop = opcode >> 26;
663     uint32 exop = (opcode >> 1) & 0x3ff;
664     uint32 ra = (opcode >> 16) & 0x1f;
665     uint32 rb = (opcode >> 11) & 0x1f;
666     uint32 rd = (opcode >> 21) & 0x1f;
667     uint32 imm = opcode & 0xffff;
668    
669     // Analyze opcode
670     enum {
671     TYPE_UNKNOWN,
672     TYPE_LOAD,
673     TYPE_STORE
674     } transfer_type = TYPE_UNKNOWN;
675     enum {
676     SIZE_UNKNOWN,
677     SIZE_BYTE,
678     SIZE_HALFWORD,
679     SIZE_WORD
680     } transfer_size = SIZE_UNKNOWN;
681     enum {
682     MODE_UNKNOWN,
683     MODE_NORM,
684     MODE_U,
685     MODE_X,
686     MODE_UX
687     } addr_mode = MODE_UNKNOWN;
688     switch (primop) {
689     case 31:
690     switch (exop) {
691     case 23: // lwzx
692     transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_X; break;
693     case 55: // lwzux
694     transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break;
695     case 87: // lbzx
696     transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break;
697     case 119: // lbzux
698     transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break;
699     case 151: // stwx
700     transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_X; break;
701     case 183: // stwux
702     transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_UX; break;
703     case 215: // stbx
704     transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_X; break;
705     case 247: // stbux
706     transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_UX; break;
707     case 279: // lhzx
708     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
709     case 311: // lhzux
710     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
711     case 343: // lhax
712     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
713     case 375: // lhaux
714     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
715     case 407: // sthx
716     transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_X; break;
717     case 439: // sthux
718     transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_UX; break;
719     }
720     break;
721    
722     case 32: // lwz
723     transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break;
724     case 33: // lwzu
725     transfer_type = TYPE_LOAD; transfer_size = SIZE_WORD; addr_mode = MODE_U; break;
726     case 34: // lbz
727     transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break;
728     case 35: // lbzu
729     transfer_type = TYPE_LOAD; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break;
730     case 36: // stw
731     transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_NORM; break;
732     case 37: // stwu
733     transfer_type = TYPE_STORE; transfer_size = SIZE_WORD; addr_mode = MODE_U; break;
734     case 38: // stb
735     transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_NORM; break;
736     case 39: // stbu
737     transfer_type = TYPE_STORE; transfer_size = SIZE_BYTE; addr_mode = MODE_U; break;
738     case 40: // lhz
739     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
740     case 41: // lhzu
741     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
742     case 42: // lha
743     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
744     case 43: // lhau
745     transfer_type = TYPE_LOAD; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
746     case 44: // sth
747     transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_NORM; break;
748     case 45: // sthu
749     transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break;
750     }
751    
752     // Calculate effective address
753     uint32 addr = 0;
754     switch (addr_mode) {
755     case MODE_X:
756     case MODE_UX:
757     if (ra == 0)
758     addr = segv_r[rb];
759     else
760     addr = segv_r[ra] + segv_r[rb];
761     break;
762     case MODE_NORM:
763     case MODE_U:
764     if (ra == 0)
765     addr = (int32)(int16)imm;
766     else
767     addr = segv_r[ra] + (int32)(int16)imm;
768     break;
769     }
770    
771     // Ignore ROM writes
772     if (transfer_type == TYPE_STORE && addr >= (uint32)ROMBaseHost && addr < (uint32)ROMBaseHost + ROMSize) {
773     // 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));
774     if (addr_mode == MODE_U || addr_mode == MODE_UX)
775     segv_r[ra] = addr;
776     r->pc += 4;
777     goto rti;
778     }
779    
780     // For all other errors, jump into debugger
781     char str[256];
782     sprintf(str, "SIGSEGV\n"
783     " pc %08lx lr %08lx ctr %08lx msr %08lx\n"
784     " xer %08lx cr %08lx fpscr %08lx\n"
785     " r0 %08lx r1 %08lx r2 %08lx r3 %08lx\n"
786     " r4 %08lx r5 %08lx r6 %08lx r7 %08lx\n"
787     " r8 %08lx r9 %08lx r10 %08lx r11 %08lx\n"
788     " r12 %08lx r13 %08lx r14 %08lx r15 %08lx\n"
789     " r16 %08lx r17 %08lx r18 %08lx r19 %08lx\n"
790     " r20 %08lx r21 %08lx r22 %08lx r23 %08lx\n"
791     " r24 %08lx r25 %08lx r26 %08lx r27 %08lx\n"
792     " r28 %08lx r29 %08lx r30 %08lx r31 %08lx\n",
793     r->pc, r->lr, r->ctr, r->msr,
794     r->xer, r->cr, r->fpscr,
795     r->r0, r->r1, r->r2, r->r3,
796     r->r4, r->r5, r->r6, r->r7,
797     r->r8, r->r9, r->r10, r->r11,
798     r->r12, segv_r[13], segv_r[14], segv_r[15],
799     segv_r[16], segv_r[17], segv_r[18], segv_r[19],
800     segv_r[20], segv_r[21], segv_r[22], segv_r[23],
801     segv_r[24], segv_r[25], segv_r[26], segv_r[27],
802     segv_r[28], segv_r[29], segv_r[30], segv_r[31]);
803     disable_debugger(false);
804     debugger(str);
805     QuitEmulator();
806     return;
807    
808     rti:
809     // Restore volatile registers
810     r->r0 = segv_r[0];
811     r->r1 = segv_r[1];
812     r->r2 = segv_r[2];
813     r->r3 = segv_r[3];
814     r->r4 = segv_r[4];
815     r->r5 = segv_r[5];
816     r->r6 = segv_r[6];
817     r->r7 = segv_r[7];
818     r->r8 = segv_r[8];
819     r->r9 = segv_r[9];
820     r->r10 = segv_r[10];
821     r->r11 = segv_r[11];
822     r->r12 = segv_r[12];
823     }
824     #endif