ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/BeOS/main_beos.cpp
Revision: 1.1
Committed: 1999-10-03T14:16:25Z (25 years, 1 month ago) by cebix
Branch: MAIN
Branch point for: cebix
Log Message:
Initial revision

File Contents

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