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 (15 years, 3 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

# User Rev Content
1 cebix 1.1 /*
2     * emul_op.cpp - 68k opcodes for ROM patches
3     *
4 gbeauche 1.23 * SheepShaver (C) 1997-2008 Christian Bauer and Marc Hellwig
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 <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 gbeauche 1.5 #include "thunks.h"
49 cebix 1.1
50     #define DEBUG 0
51     #include "debug.h"
52    
53    
54     // TVector of MakeExecutable
55 gbeauche 1.15 static uint32 MakeExecutableTvec;
56 cebix 1.1
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 gbeauche 1.17 MakeExecutableTvec = FindLibSymbol("\023PrivateInterfaceLib", "\016MakeExecutable");
256 gbeauche 1.15 D(bug("MakeExecutable TVECT at %08x\n", MakeExecutableTvec));
257     WriteMacInt32(MakeExecutableTvec, NativeFunction(NATIVE_MAKE_EXECUTABLE));
258 gbeauche 1.5 #if !EMULATED_PPC
259 gbeauche 1.15 WriteMacInt32(MakeExecutableTvec + 4, (uint32)TOC);
260 cebix 1.1 #endif
261    
262     // Patch DebugStr()
263 gbeauche 1.16 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 cebix 1.1 };
268 gbeauche 1.16 BUILD_SHEEPSHAVER_PROCEDURE(proc);
269     WriteMacInt32(0x1dfc, proc);
270 cebix 1.1 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 gbeauche 1.13
285 gbeauche 1.20 // Enable DR emulator (disabled for now)
286     if (PrefsFindBool("jit68k") && 0) {
287 gbeauche 1.13 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 asvitkine 1.24 memcpy((void *)DR_EMULATOR_BASE, (void *)(ROMBase + 0x370000), DR_EMULATOR_SIZE);
294 gbeauche 1.15 MakeExecutable(0, DR_EMULATOR_BASE, DR_EMULATOR_SIZE);
295 gbeauche 1.13 }
296 cebix 1.1 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 gbeauche 1.2 ExecuteNative(NATIVE_VIDEO_VBL);
308 cebix 1.1
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 gbeauche 1.2 ExecuteNative(NATIVE_ETHER_IRQ);
326 cebix 1.1 }
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 gbeauche 1.22 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 cebix 1.1 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 gbeauche 1.14 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 cebix 1.1 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 gbeauche 1.9 if (ReadMacInt32(0x14c) == 0)
476 gbeauche 1.21 idle_wait();
477 cebix 1.1 r->a[0] = ReadMacInt32(0x2b6);
478     break;
479    
480 gbeauche 1.9 case OP_IDLE_TIME_2:
481     // Sleep if no events pending
482     if (ReadMacInt32(0x14c) == 0)
483 gbeauche 1.21 idle_wait();
484 gbeauche 1.9 r->d[0] = (uint32)-2;
485     break;
486    
487 cebix 1.1 default:
488     printf("FATAL: EMUL_OP called with bogus selector %08x\n", selector);
489     QuitEmulator();
490     break;
491     }
492     }