ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/emul_op.cpp
Revision: 1.24
Committed: 2009-08-18T18:26:10Z (14 years, 8 months ago) by asvitkine
Branch: MAIN
Changes since 1.23: +1 -1 lines
Log Message:
[Michael Schmitt]
Attached is a patch to SheepShaver to fix memory allocation problems when OS X 10.5 is the host. It also relaxes the 512 MB RAM limit on OS X hosts.


Problem
-------
Some users have been unable to run SheepShaver on OS X 10.5 (Leopard) hosts. The symptom is error "ERROR: Cannot map RAM: File already exists".

SheepShaver allocates RAM at fixed addresses. If it is running in "Real" addressing mode, and can't allocate at address 0, then it was hard-coded to allocate the RAM area at 0x20000000. The ROM area as allocated at 0x40800000.

The normal configuration is for SheepShaver to run under SDL, which is a Cocoa wrapper. By the time SheepShaver does its memory allocations, the Cocoa application has already started. The result is the SheepShaver memory address space already contains libraries, fonts, Input Managers, and IOKit areas.

On Leopard hosts these areas can land on the same addresses SheepShaver needs, so SheepShaver's memory allocation fails.


Solution
--------
The approach is to change SheepShaver (on Unix & OS X hosts) to allocate the RAM area anywhere it can find the space, rather than at a fixed address.

This could result in the RAM allocated higher than the ROM area, which causes a crash. To prevent this from occurring, the RAM and ROM areas are allocated contiguously.

Previously the ROM starting address was a constant ROM_BASE, which was used throughout the source files. The ROM start address is now a variable ROMBase. ROMBase is allocated and set by main_*.cpp just like RAMBase.

A side-effect of this change is that it lifts the 512 MB RAM limit for OS X hosts. The limit was because the fixed RAM and ROM addresses were such that the RAM could only be 512 MB before it overlapped the ROM area.


Impact
------
The change to make ROMBase a variable is throughout all hosts & addressing modes.

The RAM and ROM areas will only shift when run on Unix & OS X hosts, otherwise the same fixed allocation address is used as before.

This change is limited to "Real" addressing mode. Unlike Basilisk II, SheepShaver *pre-calculates* the offset for "Direct" addressing mode; the offset is compiled into the program. If the RAM address were allowed to shift, it could result in the RAM area wrapping around address 0.


Changes to main_unix.cpp
------------------------
1. Real addressing mode no longer defines a RAM_BASE constant.

2. The base address of the Mac ROM (ROMBase) is defined and exported by this program.

3. Memory management helper vm_mac_acquire is renamed to vm_mac_acquire_fixed. Added a new memory management helper vm_mac_acquire, which allocates memory at any address.

4. Changed and rearranged the allocation of RAM and ROM areas.

Before it worked like this:

  - Allocate ROM area
  - If can, attempt to allocate RAM at address zero
  - If RAM not allocated at 0, allocate at fixed address

We still want to try allocating the RAM at zero, and if using DIRECT addressing we're still going to use the fixed addresses. So we don't know where the ROM should be until after we do the RAM. The new logic is:

  - If can, attempt to allocate RAM at address zero
  - If RAM not allocated at 0
      if REAL addressing
         allocate RAM and ROM together. The ROM address is aligned to a 1 MB boundary
      else (direct addressing)
         allocate RAM at fixed address
  - If ROM hasn't been allocated yet, allocate at fixed address

5. Calculate ROMBase and ROMBaseHost based on where the ROM was loaded.

6. There is a crash if the RAM is allocated too high. To try and catch this, check if it was allocated higher than the kernel data address.

7. Change subsequent code from using constant ROM_BASE to variable ROMBase.


Changes to Other Programs
-------------------------
emul_op.cpp, main.cpp, name_registery.cpp, rom_patches.cpp, rsrc_patches.cpp, emul_ppc.cpp, sheepshaver_glue.cpp, ppc-translate-cpp:
Change from constant ROM_BASE to variable ROMBase.

ppc_asm.S: It was setting register to a hard-coded literal address: 0x40b0d000. Changed to set it to ROMBase + 0x30d000.

ppc_asm.tmpl: It defined a macro ASM_LO16 but it assumed that the macro would always be used with operands that included a register specification. This is not true. Moved the register specification from the macro to the macro invocations.

main_beos.cpp, main_windows.cpp: Since the subprograms are all expecting a variable ROMBase, all the main_*.cpp pgrams have to define and export it. The ROM_BASE constant is moved here for consistency. The mains for beos and windows just allocate the ROM at the same fixed address as before, set ROMBaseHost and ROMBase to that address, and then use ROMBase for the subsequent code.

cpu_emulation.h: removed ROM_BASE constant. This value is moved to the main_*.cpp modules, to be consistent with RAM_BASE.

user_strings_unix.cpp, user_strings_unix.h: Added new error messages related to errors that occur when the RAM and ROM are allocated anywhere.

File Contents

# Content
1 /*
2 * emul_op.cpp - 68k opcodes for ROM patches
3 *
4 * SheepShaver (C) 1997-2008 Christian Bauer and Marc Hellwig
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 <stdio.h>
22
23 #include "sysdeps.h"
24 #include "main.h"
25 #include "version.h"
26 #include "prefs.h"
27 #include "cpu_emulation.h"
28 #include "xlowmem.h"
29 #include "xpram.h"
30 #include "timer.h"
31 #include "adb.h"
32 #include "sony.h"
33 #include "disk.h"
34 #include "cdrom.h"
35 #include "scsi.h"
36 #include "video.h"
37 #include "audio.h"
38 #include "ether.h"
39 #include "serial.h"
40 #include "clip.h"
41 #include "extfs.h"
42 #include "macos_util.h"
43 #include "rom_patches.h"
44 #include "rsrc_patches.h"
45 #include "name_registry.h"
46 #include "user_strings.h"
47 #include "emul_op.h"
48 #include "thunks.h"
49
50 #define DEBUG 0
51 #include "debug.h"
52
53
54 // TVector of MakeExecutable
55 static uint32 MakeExecutableTvec;
56
57
58 /*
59 * Execute EMUL_OP opcode (called by 68k emulator)
60 */
61
62 void EmulOp(M68kRegisters *r, uint32 pc, int selector)
63 {
64 D(bug("EmulOp %04x at %08x\n", selector, pc));
65 switch (selector) {
66 case OP_BREAK: // Breakpoint
67 printf("*** Breakpoint\n");
68 Dump68kRegs(r);
69 break;
70
71 case OP_XPRAM1: { // Read/write from/to XPRam
72 uint32 len = r->d[3];
73 uint8 *adr = Mac2HostAddr(r->a[3]);
74 D(bug("XPRAMReadWrite d3: %08lx, a3: %p\n", len, adr));
75 int ofs = len & 0xffff;
76 len >>= 16;
77 if (len & 0x8000) {
78 len &= 0x7fff;
79 for (uint32 i=0; i<len; i++)
80 XPRAM[((ofs + i) & 0xff) + 0x1300] = *adr++;
81 } else {
82 for (uint32 i=0; i<len; i++)
83 *adr++ = XPRAM[((ofs + i) & 0xff) + 0x1300];
84 }
85 break;
86 }
87
88 case OP_XPRAM2: // Read from XPRam
89 r->d[1] = XPRAM[(r->d[1] & 0xff) + 0x1300];
90 break;
91
92 case OP_XPRAM3: // Write to XPRam
93 XPRAM[(r->d[1] & 0xff) + 0x1300] = r->d[2];
94 break;
95
96 case OP_NVRAM1: { // Read from NVRAM
97 int ofs = r->d[0];
98 r->d[0] = XPRAM[ofs & 0x1fff];
99 bool localtalk = !(XPRAM[0x13e0] || XPRAM[0x13e1]); // LocalTalk enabled?
100 switch (ofs) {
101 case 0x13e0: // Disable LocalTalk (use EtherTalk instead)
102 if (localtalk)
103 r->d[0] = 0x00;
104 break;
105 case 0x13e1:
106 if (localtalk)
107 r->d[0] = 0x01;
108 break;
109 case 0x13e2:
110 if (localtalk)
111 r->d[0] = 0x00;
112 break;
113 case 0x13e3:
114 if (localtalk)
115 r->d[0] = 0x0a;
116 break;
117 }
118 break;
119 }
120
121 case OP_NVRAM2: // Write to NVRAM
122 XPRAM[r->d[0] & 0x1fff] = r->d[1];
123 break;
124
125 case OP_NVRAM3: // Read/write from/to NVRAM
126 if (r->d[3]) {
127 r->d[0] = XPRAM[(r->d[4] + 0x1300) & 0x1fff];
128 } else {
129 XPRAM[(r->d[4] + 0x1300) & 0x1fff] = r->d[5];
130 r->d[0] = 0;
131 }
132 break;
133
134 case OP_FIX_MEMTOP: // Fixes MemTop in BootGlobs during startup
135 D(bug("Fix MemTop\n"));
136 WriteMacInt32(BootGlobsAddr - 20, RAMBase + RAMSize); // MemTop
137 r->a[6] = RAMBase + RAMSize;
138 break;
139
140 case OP_FIX_MEMSIZE: { // Fixes physical/logical RAM size during startup
141 D(bug("Fix MemSize\n"));
142 uint32 diff = ReadMacInt32(0x1ef8) - ReadMacInt32(0x1ef4);
143 WriteMacInt32(0x1ef8, RAMSize); // Physical RAM size
144 WriteMacInt32(0x1ef4, RAMSize - diff); // Logical RAM size
145 break;
146 }
147
148 case OP_FIX_BOOTSTACK: // Fixes boot stack pointer in boot 3 resource
149 D(bug("Fix BootStack\n"));
150 r->a[1] = r->a[7] = RAMBase + RAMSize * 3 / 4;
151 break;
152
153 case OP_SONY_OPEN: // Floppy driver functions
154 r->d[0] = SonyOpen(r->a[0], r->a[1]);
155 break;
156 case OP_SONY_PRIME:
157 r->d[0] = SonyPrime(r->a[0], r->a[1]);
158 break;
159 case OP_SONY_CONTROL:
160 r->d[0] = SonyControl(r->a[0], r->a[1]);
161 break;
162 case OP_SONY_STATUS:
163 r->d[0] = SonyStatus(r->a[0], r->a[1]);
164 break;
165
166 case OP_DISK_OPEN: // Disk driver functions
167 r->d[0] = DiskOpen(r->a[0], r->a[1]);
168 break;
169 case OP_DISK_PRIME:
170 r->d[0] = DiskPrime(r->a[0], r->a[1]);
171 break;
172 case OP_DISK_CONTROL:
173 r->d[0] = DiskControl(r->a[0], r->a[1]);
174 break;
175 case OP_DISK_STATUS:
176 r->d[0] = DiskStatus(r->a[0], r->a[1]);
177 break;
178
179 case OP_CDROM_OPEN: // CD-ROM driver functions
180 r->d[0] = CDROMOpen(r->a[0], r->a[1]);
181 break;
182 case OP_CDROM_PRIME:
183 r->d[0] = CDROMPrime(r->a[0], r->a[1]);
184 break;
185 case OP_CDROM_CONTROL:
186 r->d[0] = CDROMControl(r->a[0], r->a[1]);
187 break;
188 case OP_CDROM_STATUS:
189 r->d[0] = CDROMStatus(r->a[0], r->a[1]);
190 break;
191
192 case OP_AUDIO_DISPATCH: // Audio component functions
193 r->d[0] = AudioDispatch(r->a[3], r->a[4]);
194 break;
195
196 case OP_SOUNDIN_OPEN: // Sound input driver functions
197 r->d[0] = SoundInOpen(r->a[0], r->a[1]);
198 break;
199 case OP_SOUNDIN_PRIME:
200 r->d[0] = SoundInPrime(r->a[0], r->a[1]);
201 break;
202 case OP_SOUNDIN_CONTROL:
203 r->d[0] = SoundInControl(r->a[0], r->a[1]);
204 break;
205 case OP_SOUNDIN_STATUS:
206 r->d[0] = SoundInStatus(r->a[0], r->a[1]);
207 break;
208 case OP_SOUNDIN_CLOSE:
209 r->d[0] = SoundInClose(r->a[0], r->a[1]);
210 break;
211
212 case OP_ADBOP: // ADBOp() replacement
213 ADBOp(r->d[0], Mac2HostAddr(ReadMacInt32(r->a[0])));
214 break;
215
216 case OP_INSTIME: // InsTime() replacement
217 r->d[0] = InsTime(r->a[0], r->d[1]);
218 break;
219 case OP_RMVTIME: // RmvTime() replacement
220 r->d[0] = RmvTime(r->a[0]);
221 break;
222 case OP_PRIMETIME: // PrimeTime() replacement
223 r->d[0] = PrimeTime(r->a[0], r->d[0]);
224 break;
225
226 case OP_MICROSECONDS: // Microseconds() replacement
227 Microseconds(r->a[0], r->d[0]);
228 break;
229
230 case OP_PUT_SCRAP: // PutScrap() patch
231 PutScrap(ReadMacInt32(r->a[7] + 8), Mac2HostAddr(ReadMacInt32(r->a[7] + 4)), ReadMacInt32(r->a[7] + 12));
232 break;
233
234 case OP_GET_SCRAP: // GetScrap() patch
235 GetScrap((void **)Mac2HostAddr(ReadMacInt32(r->a[7] + 4)), ReadMacInt32(r->a[7] + 8), ReadMacInt32(r->a[7] + 12));
236 break;
237
238 case OP_DEBUG_STR: // DebugStr() shows warning message
239 if (PrefsFindBool("nogui")) {
240 uint8 *pstr = Mac2HostAddr(ReadMacInt32(r->a[7] + 4));
241 char str[256];
242 int i;
243 for (i=0; i<pstr[0]; i++)
244 str[i] = pstr[i+1];
245 str[i] = 0;
246 WarningAlert(str);
247 }
248 break;
249
250 case OP_INSTALL_DRIVERS: { // Patch to install our own drivers during startup
251 // Install drivers
252 InstallDrivers();
253
254 // Patch MakeExecutable()
255 MakeExecutableTvec = FindLibSymbol("\023PrivateInterfaceLib", "\016MakeExecutable");
256 D(bug("MakeExecutable TVECT at %08x\n", MakeExecutableTvec));
257 WriteMacInt32(MakeExecutableTvec, NativeFunction(NATIVE_MAKE_EXECUTABLE));
258 #if !EMULATED_PPC
259 WriteMacInt32(MakeExecutableTvec + 4, (uint32)TOC);
260 #endif
261
262 // Patch DebugStr()
263 static const uint8 proc_template[] = {
264 M68K_EMUL_OP_DEBUG_STR >> 8, M68K_EMUL_OP_DEBUG_STR,
265 0x4e, 0x74, // rtd #4
266 0x00, 0x04
267 };
268 BUILD_SHEEPSHAVER_PROCEDURE(proc);
269 WriteMacInt32(0x1dfc, proc);
270 break;
271 }
272
273 case OP_NAME_REGISTRY: // Patch Name Registry and initialize CallUniversalProc
274 r->d[0] = (uint32)-1;
275 PatchNameRegistry();
276 InitCallUniversalProc();
277 break;
278
279 case OP_RESET: // Early in MacOS reset
280 D(bug("*** RESET ***\n"));
281 TimerReset();
282 MacOSUtilReset();
283 AudioReset();
284
285 // Enable DR emulator (disabled for now)
286 if (PrefsFindBool("jit68k") && 0) {
287 D(bug("DR activated\n"));
288 WriteMacInt32(KernelDataAddr + 0x17a0, 3); // Prepare for DR emulator activation
289 WriteMacInt32(KernelDataAddr + 0x17c0, DR_CACHE_BASE);
290 WriteMacInt32(KernelDataAddr + 0x17c4, DR_CACHE_SIZE);
291 WriteMacInt32(KernelDataAddr + 0x1b04, DR_CACHE_BASE);
292 WriteMacInt32(KernelDataAddr + 0x1b00, DR_EMULATOR_BASE);
293 memcpy((void *)DR_EMULATOR_BASE, (void *)(ROMBase + 0x370000), DR_EMULATOR_SIZE);
294 MakeExecutable(0, DR_EMULATOR_BASE, DR_EMULATOR_SIZE);
295 }
296 break;
297
298 case OP_IRQ: // Level 1 interrupt
299 WriteMacInt16(ReadMacInt32(KernelDataAddr + 0x67c), 0); // Clear interrupt
300 r->d[0] = 0;
301 if (HasMacStarted()) {
302 if (InterruptFlags & INTFLAG_VIA) {
303 ClearInterruptFlag(INTFLAG_VIA);
304 #if !PRECISE_TIMING
305 TimerInterrupt();
306 #endif
307 ExecuteNative(NATIVE_VIDEO_VBL);
308
309 static int tick_counter = 0;
310 if (++tick_counter >= 60) {
311 tick_counter = 0;
312 SonyInterrupt();
313 DiskInterrupt();
314 CDROMInterrupt();
315 }
316
317 r->d[0] = 1; // Flag: 68k interrupt routine executes VBLTasks etc.
318 }
319 if (InterruptFlags & INTFLAG_SERIAL) {
320 ClearInterruptFlag(INTFLAG_SERIAL);
321 SerialInterrupt();
322 }
323 if (InterruptFlags & INTFLAG_ETHER) {
324 ClearInterruptFlag(INTFLAG_ETHER);
325 ExecuteNative(NATIVE_ETHER_IRQ);
326 }
327 if (InterruptFlags & INTFLAG_TIMER) {
328 ClearInterruptFlag(INTFLAG_TIMER);
329 TimerInterrupt();
330 }
331 if (InterruptFlags & INTFLAG_AUDIO) {
332 ClearInterruptFlag(INTFLAG_AUDIO);
333 AudioInterrupt();
334 }
335 if (InterruptFlags & INTFLAG_ADB) {
336 ClearInterruptFlag(INTFLAG_ADB);
337 ADBInterrupt();
338 }
339 } else
340 r->d[0] = 1;
341 break;
342
343 case OP_SCSI_DISPATCH: { // SCSIDispatch() replacement
344 uint32 ret = ReadMacInt32(r->a[7]);
345 uint16 sel = ReadMacInt16(r->a[7] + 4);
346 r->a[7] += 6;
347 // D(bug("SCSIDispatch(%d)\n", sel));
348 int stack;
349 switch (sel) {
350 case 0: // SCSIReset
351 WriteMacInt16(r->a[7], SCSIReset());
352 stack = 0;
353 break;
354 case 1: // SCSIGet
355 WriteMacInt16(r->a[7], SCSIGet());
356 stack = 0;
357 break;
358 case 2: // SCSISelect
359 case 11: // SCSISelAtn
360 WriteMacInt16(r->a[7] + 2, SCSISelect(ReadMacInt8(r->a[7] + 1)));
361 stack = 2;
362 break;
363 case 3: // SCSICmd
364 WriteMacInt16(r->a[7] + 6, SCSICmd(ReadMacInt16(r->a[7]), Mac2HostAddr(ReadMacInt32(r->a[7] + 2))));
365 stack = 6;
366 break;
367 case 4: // SCSIComplete
368 WriteMacInt16(r->a[7] + 12, SCSIComplete(ReadMacInt32(r->a[7]), ReadMacInt32(r->a[7] + 4), ReadMacInt32(r->a[7] + 8)));
369 stack = 12;
370 break;
371 case 5: // SCSIRead
372 case 8: // SCSIRBlind
373 WriteMacInt16(r->a[7] + 4, SCSIRead(ReadMacInt32(r->a[7])));
374 stack = 4;
375 break;
376 case 6: // SCSIWrite
377 case 9: // SCSIWBlind
378 WriteMacInt16(r->a[7] + 4, SCSIWrite(ReadMacInt32(r->a[7])));
379 stack = 4;
380 break;
381 case 10: // SCSIStat
382 WriteMacInt16(r->a[7], SCSIStat());
383 stack = 0;
384 break;
385 case 12: // SCSIMsgIn
386 WriteMacInt16(r->a[7] + 4, 0);
387 stack = 4;
388 break;
389 case 13: // SCSIMsgOut
390 WriteMacInt16(r->a[7] + 2, 0);
391 stack = 2;
392 break;
393 case 14: // SCSIMgrBusy
394 WriteMacInt16(r->a[7], SCSIMgrBusy());
395 stack = 0;
396 break;
397 default:
398 printf("FATAL: SCSIDispatch: illegal selector\n");
399 stack = 0;
400 //!! SysError(12)
401 }
402 r->a[0] = ret;
403 r->a[7] += stack;
404 break;
405 }
406
407 case OP_SCSI_ATOMIC: // SCSIAtomic() replacement
408 D(bug("SCSIAtomic\n"));
409 r->d[0] = (uint32)-7887;
410 break;
411
412 case OP_CHECK_SYSV: { // Check we are not using MacOS < 8.1 with a NewWorld ROM
413 r->a[1] = r->d[1];
414 r->a[0] = ReadMacInt32(r->d[1]);
415 uint32 sysv = ReadMacInt16(r->a[0]);
416 D(bug("Detected MacOS version %d.%d.%d\n", (sysv >> 8) & 0xf, (sysv >> 4) & 0xf, sysv & 0xf));
417 if (ROMType == ROMTYPE_NEWWORLD && sysv < 0x0801)
418 r->d[1] = 0;
419 break;
420 }
421
422 case OP_NTRB_17_PATCH:
423 r->a[2] = ReadMacInt32(r->a[7]);
424 r->a[7] += 4;
425 if (ReadMacInt16(r->a[2] + 6) == 17)
426 PatchNativeResourceManager();
427 break;
428
429 case OP_NTRB_17_PATCH2:
430 r->a[7] += 8;
431 PatchNativeResourceManager();
432 break;
433
434 case OP_NTRB_17_PATCH3:
435 r->a[2] = ReadMacInt32(r->a[7]);
436 r->a[7] += 4;
437 D(bug("%d %d\n", ReadMacInt16(r->a[2]), ReadMacInt16(r->a[2] + 6)));
438 if (ReadMacInt16(r->a[2]) == 11 && ReadMacInt16(r->a[2] + 6) == 17)
439 PatchNativeResourceManager();
440 break;
441
442 case OP_NTRB_17_PATCH4:
443 r->d[0] = ReadMacInt16(r->a[7]);
444 r->a[7] += 2;
445 D(bug("%d %d\n", ReadMacInt16(r->a[2]), ReadMacInt16(r->a[2] + 6)));
446 if (ReadMacInt16(r->a[2]) == 11 && ReadMacInt16(r->a[2] + 6) == 17)
447 PatchNativeResourceManager();
448 break;
449
450 case OP_CHECKLOAD: { // vCheckLoad() patch
451 uint32 type = ReadMacInt32(r->a[7]);
452 r->a[7] += 4;
453 int16 id = ReadMacInt16(r->a[2]);
454 if (r->a[0] == 0)
455 break;
456 uint32 adr = ReadMacInt32(r->a[0]);
457 if (adr == 0)
458 break;
459 uint16 *p = (uint16 *)Mac2HostAddr(adr);
460 uint32 size = ReadMacInt32(adr - 8) & 0xffffff;
461 CheckLoad(type, id, p, size);
462 break;
463 }
464
465 case OP_EXTFS_COMM: // External file system routines
466 WriteMacInt16(r->a[7] + 14, ExtFSComm(ReadMacInt16(r->a[7] + 12), ReadMacInt32(r->a[7] + 8), ReadMacInt32(r->a[7] + 4)));
467 break;
468
469 case OP_EXTFS_HFS:
470 WriteMacInt16(r->a[7] + 20, ExtFSHFS(ReadMacInt32(r->a[7] + 16), ReadMacInt16(r->a[7] + 14), ReadMacInt32(r->a[7] + 10), ReadMacInt32(r->a[7] + 6), ReadMacInt16(r->a[7] + 4)));
471 break;
472
473 case OP_IDLE_TIME:
474 // Sleep if no events pending
475 if (ReadMacInt32(0x14c) == 0)
476 idle_wait();
477 r->a[0] = ReadMacInt32(0x2b6);
478 break;
479
480 case OP_IDLE_TIME_2:
481 // Sleep if no events pending
482 if (ReadMacInt32(0x14c) == 0)
483 idle_wait();
484 r->d[0] = (uint32)-2;
485 break;
486
487 default:
488 printf("FATAL: EMUL_OP called with bogus selector %08x\n", selector);
489 QuitEmulator();
490 break;
491 }
492 }