ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp
Revision: 1.17
Committed: 2003-11-10T16:23:58Z (21 years ago) by gbeauche
Branch: MAIN
Changes since 1.16: +36 -3 lines
Log Message:
Fix "ignoresegv" case to actually skip the faulty instruction. Merge
conditions to skip instruction on SIGSEGVfrom PowerPC native mode. The
instruction skipper takes care to set the output register to 0.

File Contents

# Content
1 /*
2 * sheepshaver_glue.cpp - Glue Kheperix CPU to SheepShaver CPU engine interface
3 *
4 * SheepShaver (C) 1997-2002 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 "sysdeps.h"
22 #include "cpu_emulation.h"
23 #include "main.h"
24 #include "prefs.h"
25 #include "xlowmem.h"
26 #include "emul_op.h"
27 #include "rom_patches.h"
28 #include "macos_util.h"
29 #include "block-alloc.hpp"
30 #include "sigsegv.h"
31 #include "cpu/ppc/ppc-cpu.hpp"
32 #include "cpu/ppc/ppc-operations.hpp"
33
34 // Used for NativeOp trampolines
35 #include "video.h"
36 #include "name_registry.h"
37 #include "serial.h"
38 #include "ether.h"
39
40 #include <stdio.h>
41
42 #if ENABLE_MON
43 #include "mon.h"
44 #include "mon_disass.h"
45 #endif
46
47 #define DEBUG 0
48 #include "debug.h"
49
50 // Emulation time statistics
51 #define EMUL_TIME_STATS 1
52
53 #if EMUL_TIME_STATS
54 static clock_t emul_start_time;
55 static uint32 interrupt_count = 0;
56 static clock_t interrupt_time = 0;
57 static uint32 exec68k_count = 0;
58 static clock_t exec68k_time = 0;
59 static uint32 native_exec_count = 0;
60 static clock_t native_exec_time = 0;
61 static uint32 macos_exec_count = 0;
62 static clock_t macos_exec_time = 0;
63 #endif
64
65 static void enter_mon(void)
66 {
67 // Start up mon in real-mode
68 #if ENABLE_MON
69 char *arg[4] = {"mon", "-m", "-r", NULL};
70 mon(3, arg);
71 #endif
72 }
73
74 // Enable multicore (main/interrupts) cpu emulation?
75 #define MULTICORE_CPU (ASYNC_IRQ ? 1 : 0)
76
77 // Enable Execute68k() safety checks?
78 #define SAFE_EXEC_68K 1
79
80 // Save FP state in Execute68k()?
81 #define SAVE_FP_EXEC_68K 1
82
83 // Interrupts in EMUL_OP mode?
84 #define INTERRUPTS_IN_EMUL_OP_MODE 1
85
86 // Interrupts in native mode?
87 #define INTERRUPTS_IN_NATIVE_MODE 1
88
89 // Pointer to Kernel Data
90 static KernelData * const kernel_data = (KernelData *)KERNEL_DATA_BASE;
91
92 // SIGSEGV handler
93 static sigsegv_return_t sigsegv_handler(sigsegv_address_t, sigsegv_address_t);
94
95
96 /**
97 * PowerPC emulator glue with special 'sheep' opcodes
98 **/
99
100 class sheepshaver_cpu
101 : public powerpc_cpu
102 {
103 void init_decoder();
104 void execute_sheep(uint32 opcode);
105
106 public:
107
108 // Constructor
109 sheepshaver_cpu();
110
111 // Condition Register accessors
112 uint32 get_cr() const { return cr().get(); }
113 void set_cr(uint32 v) { cr().set(v); }
114
115 // Execution loop
116 void execute(uint32 entry, bool enable_cache = false);
117
118 // Execute 68k routine
119 void execute_68k(uint32 entry, M68kRegisters *r);
120
121 // Execute ppc routine
122 void execute_ppc(uint32 entry);
123
124 // Execute MacOS/PPC code
125 uint32 execute_macos_code(uint32 tvect, int nargs, uint32 const *args);
126
127 // Resource manager thunk
128 void get_resource(uint32 old_get_resource);
129
130 // Handle MacOS interrupt
131 void interrupt(uint32 entry);
132 void handle_interrupt();
133
134 // Lazy memory allocator (one item at a time)
135 void *operator new(size_t size)
136 { return allocator_helper< sheepshaver_cpu, lazy_allocator >::allocate(); }
137 void operator delete(void *p)
138 { allocator_helper< sheepshaver_cpu, lazy_allocator >::deallocate(p); }
139 // FIXME: really make surre array allocation fail at link time?
140 void *operator new[](size_t);
141 void operator delete[](void *p);
142
143 // Make sure the SIGSEGV handler can access CPU registers
144 friend sigsegv_return_t sigsegv_handler(sigsegv_address_t, sigsegv_address_t);
145 };
146
147 lazy_allocator< sheepshaver_cpu > allocator_helper< sheepshaver_cpu, lazy_allocator >::allocator;
148
149 sheepshaver_cpu::sheepshaver_cpu()
150 : powerpc_cpu()
151 {
152 init_decoder();
153 }
154
155 void sheepshaver_cpu::init_decoder()
156 {
157 #ifndef PPC_NO_STATIC_II_INDEX_TABLE
158 static bool initialized = false;
159 if (initialized)
160 return;
161 initialized = true;
162 #endif
163
164 static const instr_info_t sheep_ii_table[] = {
165 { "sheep",
166 (execute_pmf)&sheepshaver_cpu::execute_sheep,
167 NULL,
168 D_form, 6, 0, CFLOW_JUMP | CFLOW_TRAP
169 }
170 };
171
172 const int ii_count = sizeof(sheep_ii_table)/sizeof(sheep_ii_table[0]);
173 D(bug("SheepShaver extra decode table has %d entries\n", ii_count));
174
175 for (int i = 0; i < ii_count; i++) {
176 const instr_info_t * ii = &sheep_ii_table[i];
177 init_decoder_entry(ii);
178 }
179 }
180
181 // Forward declaration for native opcode handler
182 static void NativeOp(int selector);
183
184 /* NativeOp instruction format:
185 +------------+--------------------------+--+----------+------------+
186 | 6 | |FN| OP | 2 |
187 +------------+--------------------------+--+----------+------------+
188 0 5 |6 19 20 21 25 26 31
189 */
190
191 typedef bit_field< 20, 20 > FN_field;
192 typedef bit_field< 21, 25 > NATIVE_OP_field;
193 typedef bit_field< 26, 31 > EMUL_OP_field;
194
195 // Execute SheepShaver instruction
196 void sheepshaver_cpu::execute_sheep(uint32 opcode)
197 {
198 // D(bug("Extended opcode %08x at %08x (68k pc %08x)\n", opcode, pc(), gpr(24)));
199 assert((((opcode >> 26) & 0x3f) == 6) && OP_MAX <= 64 + 3);
200
201 switch (opcode & 0x3f) {
202 case 0: // EMUL_RETURN
203 QuitEmulator();
204 break;
205
206 case 1: // EXEC_RETURN
207 spcflags().set(SPCFLAG_CPU_EXEC_RETURN);
208 break;
209
210 case 2: // EXEC_NATIVE
211 NativeOp(NATIVE_OP_field::extract(opcode));
212 if (FN_field::test(opcode))
213 pc() = lr();
214 else
215 pc() += 4;
216 break;
217
218 default: { // EMUL_OP
219 M68kRegisters r68;
220 WriteMacInt32(XLM_68K_R25, gpr(25));
221 WriteMacInt32(XLM_RUN_MODE, MODE_EMUL_OP);
222 for (int i = 0; i < 8; i++)
223 r68.d[i] = gpr(8 + i);
224 for (int i = 0; i < 7; i++)
225 r68.a[i] = gpr(16 + i);
226 r68.a[7] = gpr(1);
227 EmulOp(&r68, gpr(24), EMUL_OP_field::extract(opcode) - 3);
228 for (int i = 0; i < 8; i++)
229 gpr(8 + i) = r68.d[i];
230 for (int i = 0; i < 7; i++)
231 gpr(16 + i) = r68.a[i];
232 gpr(1) = r68.a[7];
233 WriteMacInt32(XLM_RUN_MODE, MODE_68K);
234 pc() += 4;
235 break;
236 }
237 }
238 }
239
240 // Execution loop
241 void sheepshaver_cpu::execute(uint32 entry, bool enable_cache)
242 {
243 powerpc_cpu::execute(entry, enable_cache);
244 }
245
246 // Handle MacOS interrupt
247 void sheepshaver_cpu::interrupt(uint32 entry)
248 {
249 #if EMUL_TIME_STATS
250 interrupt_count++;
251 const clock_t interrupt_start = clock();
252 #endif
253
254 #if !MULTICORE_CPU
255 // Save program counters and branch registers
256 uint32 saved_pc = pc();
257 uint32 saved_lr = lr();
258 uint32 saved_ctr= ctr();
259 uint32 saved_sp = gpr(1);
260 #endif
261
262 // Initialize stack pointer to SheepShaver alternate stack base
263 gpr(1) = SheepStack1Base - 64;
264
265 // Build trampoline to return from interrupt
266 uint32 trampoline[] = { htonl(POWERPC_EMUL_OP | 1) };
267
268 // Prepare registers for nanokernel interrupt routine
269 kernel_data->v[0x004 >> 2] = htonl(gpr(1));
270 kernel_data->v[0x018 >> 2] = htonl(gpr(6));
271
272 gpr(6) = ntohl(kernel_data->v[0x65c >> 2]);
273 assert(gpr(6) != 0);
274 WriteMacInt32(gpr(6) + 0x13c, gpr(7));
275 WriteMacInt32(gpr(6) + 0x144, gpr(8));
276 WriteMacInt32(gpr(6) + 0x14c, gpr(9));
277 WriteMacInt32(gpr(6) + 0x154, gpr(10));
278 WriteMacInt32(gpr(6) + 0x15c, gpr(11));
279 WriteMacInt32(gpr(6) + 0x164, gpr(12));
280 WriteMacInt32(gpr(6) + 0x16c, gpr(13));
281
282 gpr(1) = KernelDataAddr;
283 gpr(7) = ntohl(kernel_data->v[0x660 >> 2]);
284 gpr(8) = 0;
285 gpr(10) = (uint32)trampoline;
286 gpr(12) = (uint32)trampoline;
287 gpr(13) = get_cr();
288
289 // rlwimi. r7,r7,8,0,0
290 uint32 result = op_ppc_rlwimi::apply(gpr(7), 8, 0x80000000, gpr(7));
291 record_cr0(result);
292 gpr(7) = result;
293
294 gpr(11) = 0xf072; // MSR (SRR1)
295 cr().set((gpr(11) & 0x0fff0000) | (get_cr() & ~0x0fff0000));
296
297 // Enter nanokernel
298 execute(entry);
299
300 #if !MULTICORE_CPU
301 // Restore program counters and branch registers
302 pc() = saved_pc;
303 lr() = saved_lr;
304 ctr()= saved_ctr;
305 gpr(1) = saved_sp;
306 #endif
307
308 #if EMUL_TIME_STATS
309 interrupt_time += (clock() - interrupt_start);
310 #endif
311 }
312
313 // Execute 68k routine
314 void sheepshaver_cpu::execute_68k(uint32 entry, M68kRegisters *r)
315 {
316 #if EMUL_TIME_STATS
317 exec68k_count++;
318 const clock_t exec68k_start = clock();
319 #endif
320
321 #if SAFE_EXEC_68K
322 if (ReadMacInt32(XLM_RUN_MODE) != MODE_EMUL_OP)
323 printf("FATAL: Execute68k() not called from EMUL_OP mode\n");
324 #endif
325
326 // Save program counters and branch registers
327 uint32 saved_pc = pc();
328 uint32 saved_lr = lr();
329 uint32 saved_ctr= ctr();
330 uint32 saved_cr = get_cr();
331
332 // Create MacOS stack frame
333 // FIXME: make sure MacOS doesn't expect PPC registers to live on top
334 uint32 sp = gpr(1);
335 gpr(1) -= 56;
336 WriteMacInt32(gpr(1), sp);
337
338 // Save PowerPC registers
339 uint32 saved_GPRs[19];
340 memcpy(&saved_GPRs[0], &gpr(13), sizeof(uint32)*(32-13));
341 #if SAVE_FP_EXEC_68K
342 double saved_FPRs[18];
343 memcpy(&saved_FPRs[0], &fpr(14), sizeof(double)*(32-14));
344 #endif
345
346 // Setup registers for 68k emulator
347 cr().set(CR_SO_field<2>::mask()); // Supervisor mode
348 for (int i = 0; i < 8; i++) // d[0]..d[7]
349 gpr(8 + i) = r->d[i];
350 for (int i = 0; i < 7; i++) // a[0]..a[6]
351 gpr(16 + i) = r->a[i];
352 gpr(23) = 0;
353 gpr(24) = entry;
354 gpr(25) = ReadMacInt32(XLM_68K_R25); // MSB of SR
355 gpr(26) = 0;
356 gpr(28) = 0; // VBR
357 gpr(29) = ntohl(kernel_data->ed.v[0x74 >> 2]); // Pointer to opcode table
358 gpr(30) = ntohl(kernel_data->ed.v[0x78 >> 2]); // Address of emulator
359 gpr(31) = KernelDataAddr + 0x1000;
360
361 // Push return address (points to EXEC_RETURN opcode) on stack
362 gpr(1) -= 4;
363 WriteMacInt32(gpr(1), XLM_EXEC_RETURN_OPCODE);
364
365 // Rentering 68k emulator
366 WriteMacInt32(XLM_RUN_MODE, MODE_68K);
367
368 // Set r0 to 0 for 68k emulator
369 gpr(0) = 0;
370
371 // Execute 68k opcode
372 uint32 opcode = ReadMacInt16(gpr(24));
373 gpr(27) = (int32)(int16)ReadMacInt16(gpr(24) += 2);
374 gpr(29) += opcode * 8;
375 execute(gpr(29));
376
377 // Save r25 (contains current 68k interrupt level)
378 WriteMacInt32(XLM_68K_R25, gpr(25));
379
380 // Reentering EMUL_OP mode
381 WriteMacInt32(XLM_RUN_MODE, MODE_EMUL_OP);
382
383 // Save 68k registers
384 for (int i = 0; i < 8; i++) // d[0]..d[7]
385 r->d[i] = gpr(8 + i);
386 for (int i = 0; i < 7; i++) // a[0]..a[6]
387 r->a[i] = gpr(16 + i);
388
389 // Restore PowerPC registers
390 memcpy(&gpr(13), &saved_GPRs[0], sizeof(uint32)*(32-13));
391 #if SAVE_FP_EXEC_68K
392 memcpy(&fpr(14), &saved_FPRs[0], sizeof(double)*(32-14));
393 #endif
394
395 // Cleanup stack
396 gpr(1) += 56;
397
398 // Restore program counters and branch registers
399 pc() = saved_pc;
400 lr() = saved_lr;
401 ctr()= saved_ctr;
402 set_cr(saved_cr);
403
404 #if EMUL_TIME_STATS
405 exec68k_time += (clock() - exec68k_start);
406 #endif
407 }
408
409 // Call MacOS PPC code
410 uint32 sheepshaver_cpu::execute_macos_code(uint32 tvect, int nargs, uint32 const *args)
411 {
412 #if EMUL_TIME_STATS
413 macos_exec_count++;
414 const clock_t macos_exec_start = clock();
415 #endif
416
417 // Save program counters and branch registers
418 uint32 saved_pc = pc();
419 uint32 saved_lr = lr();
420 uint32 saved_ctr= ctr();
421
422 // Build trampoline with EXEC_RETURN
423 uint32 trampoline[] = { htonl(POWERPC_EMUL_OP | 1) };
424 lr() = (uint32)trampoline;
425
426 gpr(1) -= 64; // Create stack frame
427 uint32 proc = ReadMacInt32(tvect); // Get routine address
428 uint32 toc = ReadMacInt32(tvect + 4); // Get TOC pointer
429
430 // Save PowerPC registers
431 uint32 regs[8];
432 regs[0] = gpr(2);
433 for (int i = 0; i < nargs; i++)
434 regs[i + 1] = gpr(i + 3);
435
436 // Prepare and call MacOS routine
437 gpr(2) = toc;
438 for (int i = 0; i < nargs; i++)
439 gpr(i + 3) = args[i];
440 execute(proc);
441 uint32 retval = gpr(3);
442
443 // Restore PowerPC registers
444 for (int i = 0; i <= nargs; i++)
445 gpr(i + 2) = regs[i];
446
447 // Cleanup stack
448 gpr(1) += 64;
449
450 // Restore program counters and branch registers
451 pc() = saved_pc;
452 lr() = saved_lr;
453 ctr()= saved_ctr;
454
455 #if EMUL_TIME_STATS
456 macos_exec_time += (clock() - macos_exec_start);
457 #endif
458
459 return retval;
460 }
461
462 // Execute ppc routine
463 inline void sheepshaver_cpu::execute_ppc(uint32 entry)
464 {
465 // Save branch registers
466 uint32 saved_lr = lr();
467
468 const uint32 trampoline[] = { htonl(POWERPC_EMUL_OP | 1) };
469 lr() = (uint32)trampoline;
470
471 execute(entry);
472
473 // Restore branch registers
474 lr() = saved_lr;
475 }
476
477 // Resource Manager thunk
478 extern "C" void check_load_invoc(uint32 type, int16 id, uint32 h);
479
480 inline void sheepshaver_cpu::get_resource(uint32 old_get_resource)
481 {
482 uint32 type = gpr(3);
483 int16 id = gpr(4);
484
485 // Create stack frame
486 gpr(1) -= 56;
487
488 // Call old routine
489 execute_ppc(old_get_resource);
490
491 // Call CheckLoad()
492 uint32 handle = gpr(3);
493 check_load_invoc(type, id, handle);
494 gpr(3) = handle;
495
496 // Cleanup stack
497 gpr(1) += 56;
498 }
499
500
501 /**
502 * SheepShaver CPU engine interface
503 **/
504
505 static sheepshaver_cpu *main_cpu = NULL; // CPU emulator to handle usual control flow
506 static sheepshaver_cpu *interrupt_cpu = NULL; // CPU emulator to handle interrupts
507 static sheepshaver_cpu *current_cpu = NULL; // Current CPU emulator context
508
509 void FlushCodeCache(uintptr start, uintptr end)
510 {
511 D(bug("FlushCodeCache(%08x, %08x)\n", start, end));
512 main_cpu->invalidate_cache_range(start, end);
513 #if MULTICORE_CPU
514 interrupt_cpu->invalidate_cache_range(start, end);
515 #endif
516 }
517
518 static inline void cpu_push(sheepshaver_cpu *new_cpu)
519 {
520 #if MULTICORE_CPU
521 current_cpu = new_cpu;
522 #endif
523 }
524
525 static inline void cpu_pop()
526 {
527 #if MULTICORE_CPU
528 current_cpu = main_cpu;
529 #endif
530 }
531
532 // Dump PPC registers
533 static void dump_registers(void)
534 {
535 current_cpu->dump_registers();
536 }
537
538 // Dump log
539 static void dump_log(void)
540 {
541 current_cpu->dump_log();
542 }
543
544 /*
545 * Initialize CPU emulation
546 */
547
548 static sigsegv_return_t sigsegv_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
549 {
550 #if ENABLE_VOSF
551 // Handle screen fault
552 extern bool Screen_fault_handler(sigsegv_address_t, sigsegv_address_t);
553 if (Screen_fault_handler(fault_address, fault_instruction))
554 return SIGSEGV_RETURN_SUCCESS;
555 #endif
556
557 const uintptr addr = (uintptr)fault_address;
558 #if HAVE_SIGSEGV_SKIP_INSTRUCTION
559 // Ignore writes to ROM
560 if ((addr - ROM_BASE) < ROM_SIZE)
561 return SIGSEGV_RETURN_SKIP_INSTRUCTION;
562
563 // Get program counter of target CPU
564 sheepshaver_cpu * const cpu = current_cpu;
565 const uint32 pc = cpu->pc();
566
567 // Fault in Mac ROM or RAM?
568 bool mac_fault = (pc >= ROM_BASE) && (pc < (ROM_BASE + ROM_AREA_SIZE)) || (pc >= RAMBase) && (pc < (RAMBase + RAMSize));
569 if (mac_fault) {
570
571 // "VM settings" during MacOS 8 installation
572 if (pc == ROM_BASE + 0x488160 && cpu->gpr(20) == 0xf8000000)
573 return SIGSEGV_RETURN_SKIP_INSTRUCTION;
574
575 // MacOS 8.5 installation
576 else if (pc == ROM_BASE + 0x488140 && cpu->gpr(16) == 0xf8000000)
577 return SIGSEGV_RETURN_SKIP_INSTRUCTION;
578
579 // MacOS 8 serial drivers on startup
580 else if (pc == ROM_BASE + 0x48e080 && (cpu->gpr(8) == 0xf3012002 || cpu->gpr(8) == 0xf3012000))
581 return SIGSEGV_RETURN_SKIP_INSTRUCTION;
582
583 // MacOS 8.1 serial drivers on startup
584 else if (pc == ROM_BASE + 0x48c5e0 && (cpu->gpr(20) == 0xf3012002 || cpu->gpr(20) == 0xf3012000))
585 return SIGSEGV_RETURN_SKIP_INSTRUCTION;
586 else if (pc == ROM_BASE + 0x4a10a0 && (cpu->gpr(20) == 0xf3012002 || cpu->gpr(20) == 0xf3012000))
587 return SIGSEGV_RETURN_SKIP_INSTRUCTION;
588
589 // Ignore all other faults, if requested
590 if (PrefsFindBool("ignoresegv"))
591 return SIGSEGV_RETURN_SKIP_INSTRUCTION;
592 }
593 #else
594 #error "FIXME: You don't have the capability to skip instruction within signal handlers"
595 #endif
596
597 printf("SIGSEGV\n");
598 printf(" pc %p\n", fault_instruction);
599 printf(" ea %p\n", fault_address);
600 printf(" cpu %s\n", current_cpu == main_cpu ? "main" : "interrupts");
601 dump_registers();
602 current_cpu->dump_log();
603 enter_mon();
604 QuitEmulator();
605
606 return SIGSEGV_RETURN_FAILURE;
607 }
608
609 void init_emul_ppc(void)
610 {
611 // Initialize main CPU emulator
612 main_cpu = new sheepshaver_cpu();
613 main_cpu->set_register(powerpc_registers::GPR(3), any_register((uint32)ROM_BASE + 0x30d000));
614 WriteMacInt32(XLM_RUN_MODE, MODE_68K);
615
616 #if MULTICORE_CPU
617 // Initialize alternate CPU emulator to handle interrupts
618 interrupt_cpu = new sheepshaver_cpu();
619 #endif
620
621 // Install the handler for SIGSEGV
622 sigsegv_install_handler(sigsegv_handler);
623
624 #if ENABLE_MON
625 // Install "regs" command in cxmon
626 mon_add_command("regs", dump_registers, "regs Dump PowerPC registers\n");
627 mon_add_command("log", dump_log, "log Dump PowerPC emulation log\n");
628 #endif
629
630 #if EMUL_TIME_STATS
631 emul_start_time = clock();
632 #endif
633 }
634
635 /*
636 * Deinitialize emulation
637 */
638
639 void exit_emul_ppc(void)
640 {
641 #if EMUL_TIME_STATS
642 clock_t emul_end_time = clock();
643
644 printf("### Statistics for SheepShaver emulation parts\n");
645 const clock_t emul_time = emul_end_time - emul_start_time;
646 printf("Total emulation time : %.1f sec\n", double(emul_time) / double(CLOCKS_PER_SEC));
647 printf("Total interrupt count: %d (%2.1f Hz)\n", interrupt_count,
648 (double(interrupt_count) * CLOCKS_PER_SEC) / double(emul_time));
649
650 #define PRINT_STATS(LABEL, VAR_PREFIX) do { \
651 printf("Total " LABEL " count : %d\n", VAR_PREFIX##_count); \
652 printf("Total " LABEL " time : %.1f sec (%.1f%%)\n", \
653 double(VAR_PREFIX##_time) / double(CLOCKS_PER_SEC), \
654 100.0 * double(VAR_PREFIX##_time) / double(emul_time)); \
655 } while (0)
656
657 PRINT_STATS("Execute68k[Trap] execution", exec68k);
658 PRINT_STATS("NativeOp execution", native_exec);
659 PRINT_STATS("MacOS routine execution", macos_exec);
660
661 #undef PRINT_STATS
662 printf("\n");
663 #endif
664
665 delete main_cpu;
666 #if MULTICORE_CPU
667 delete interrupt_cpu;
668 #endif
669 }
670
671 /*
672 * Emulation loop
673 */
674
675 void emul_ppc(uint32 entry)
676 {
677 current_cpu = main_cpu;
678 #if DEBUG
679 current_cpu->start_log();
680 #endif
681 // start emulation loop and enable code translation or caching
682 current_cpu->execute(entry, true);
683 }
684
685 /*
686 * Handle PowerPC interrupt
687 */
688
689 #if ASYNC_IRQ
690 void HandleInterrupt(void)
691 {
692 main_cpu->handle_interrupt();
693 }
694 #else
695 void TriggerInterrupt(void)
696 {
697 #if 0
698 WriteMacInt32(0x16a, ReadMacInt32(0x16a) + 1);
699 #else
700 // Trigger interrupt to main cpu only
701 if (main_cpu)
702 main_cpu->trigger_interrupt();
703 #endif
704 }
705 #endif
706
707 void sheepshaver_cpu::handle_interrupt(void)
708 {
709 // Do nothing if interrupts are disabled
710 if (*(int32 *)XLM_IRQ_NEST > 0)
711 return;
712
713 // Do nothing if there is no interrupt pending
714 if (InterruptFlags == 0)
715 return;
716
717 // Disable MacOS stack sniffer
718 WriteMacInt32(0x110, 0);
719
720 // Interrupt action depends on current run mode
721 switch (ReadMacInt32(XLM_RUN_MODE)) {
722 case MODE_68K:
723 // 68k emulator active, trigger 68k interrupt level 1
724 assert(current_cpu == main_cpu);
725 WriteMacInt16(tswap32(kernel_data->v[0x67c >> 2]), 1);
726 set_cr(get_cr() | tswap32(kernel_data->v[0x674 >> 2]));
727 break;
728
729 #if INTERRUPTS_IN_NATIVE_MODE
730 case MODE_NATIVE:
731 // 68k emulator inactive, in nanokernel?
732 assert(current_cpu == main_cpu);
733 if (gpr(1) != KernelDataAddr) {
734 // Prepare for 68k interrupt level 1
735 WriteMacInt16(tswap32(kernel_data->v[0x67c >> 2]), 1);
736 WriteMacInt32(tswap32(kernel_data->v[0x658 >> 2]) + 0xdc,
737 ReadMacInt32(tswap32(kernel_data->v[0x658 >> 2]) + 0xdc)
738 | tswap32(kernel_data->v[0x674 >> 2]));
739
740 // Execute nanokernel interrupt routine (this will activate the 68k emulator)
741 DisableInterrupt();
742 cpu_push(interrupt_cpu);
743 if (ROMType == ROMTYPE_NEWWORLD)
744 current_cpu->interrupt(ROM_BASE + 0x312b1c);
745 else
746 current_cpu->interrupt(ROM_BASE + 0x312a3c);
747 cpu_pop();
748 }
749 break;
750 #endif
751
752 #if INTERRUPTS_IN_EMUL_OP_MODE
753 case MODE_EMUL_OP:
754 // 68k emulator active, within EMUL_OP routine, execute 68k interrupt routine directly when interrupt level is 0
755 if ((ReadMacInt32(XLM_68K_R25) & 7) == 0) {
756 #if 1
757 // Execute full 68k interrupt routine
758 M68kRegisters r;
759 uint32 old_r25 = ReadMacInt32(XLM_68K_R25); // Save interrupt level
760 WriteMacInt32(XLM_68K_R25, 0x21); // Execute with interrupt level 1
761 static const uint8 proc[] = {
762 0x3f, 0x3c, 0x00, 0x00, // move.w #$0000,-(sp) (fake format word)
763 0x48, 0x7a, 0x00, 0x0a, // pea @1(pc) (return address)
764 0x40, 0xe7, // move sr,-(sp) (saved SR)
765 0x20, 0x78, 0x00, 0x064, // move.l $64,a0
766 0x4e, 0xd0, // jmp (a0)
767 M68K_RTS >> 8, M68K_RTS & 0xff // @1
768 };
769 Execute68k((uint32)proc, &r);
770 WriteMacInt32(XLM_68K_R25, old_r25); // Restore interrupt level
771 #else
772 // Only update cursor
773 if (HasMacStarted()) {
774 if (InterruptFlags & INTFLAG_VIA) {
775 ClearInterruptFlag(INTFLAG_VIA);
776 ADBInterrupt();
777 ExecutePPC(VideoVBL);
778 }
779 }
780 #endif
781 }
782 break;
783 #endif
784 }
785 }
786
787 /*
788 * Execute NATIVE_OP opcode (called by PowerPC emulator)
789 */
790
791 #define POWERPC_NATIVE_OP_INIT(LR, OP) \
792 tswap32(POWERPC_EMUL_OP | ((LR) << 11) | (((uint32)OP) << 6) | 2)
793
794 // FIXME: Make sure 32-bit relocations are used
795 const uint32 NativeOpTable[NATIVE_OP_MAX] = {
796 POWERPC_NATIVE_OP_INIT(1, NATIVE_PATCH_NAME_REGISTRY),
797 POWERPC_NATIVE_OP_INIT(1, NATIVE_VIDEO_INSTALL_ACCEL),
798 POWERPC_NATIVE_OP_INIT(1, NATIVE_VIDEO_VBL),
799 POWERPC_NATIVE_OP_INIT(1, NATIVE_VIDEO_DO_DRIVER_IO),
800 POWERPC_NATIVE_OP_INIT(1, NATIVE_ETHER_IRQ),
801 POWERPC_NATIVE_OP_INIT(1, NATIVE_ETHER_INIT),
802 POWERPC_NATIVE_OP_INIT(1, NATIVE_ETHER_TERM),
803 POWERPC_NATIVE_OP_INIT(1, NATIVE_ETHER_OPEN),
804 POWERPC_NATIVE_OP_INIT(1, NATIVE_ETHER_CLOSE),
805 POWERPC_NATIVE_OP_INIT(1, NATIVE_ETHER_WPUT),
806 POWERPC_NATIVE_OP_INIT(1, NATIVE_ETHER_RSRV),
807 POWERPC_NATIVE_OP_INIT(1, NATIVE_SERIAL_NOTHING),
808 POWERPC_NATIVE_OP_INIT(1, NATIVE_SERIAL_OPEN),
809 POWERPC_NATIVE_OP_INIT(1, NATIVE_SERIAL_PRIME_IN),
810 POWERPC_NATIVE_OP_INIT(1, NATIVE_SERIAL_PRIME_OUT),
811 POWERPC_NATIVE_OP_INIT(1, NATIVE_SERIAL_CONTROL),
812 POWERPC_NATIVE_OP_INIT(1, NATIVE_SERIAL_STATUS),
813 POWERPC_NATIVE_OP_INIT(1, NATIVE_SERIAL_CLOSE),
814 POWERPC_NATIVE_OP_INIT(1, NATIVE_GET_RESOURCE),
815 POWERPC_NATIVE_OP_INIT(1, NATIVE_GET_1_RESOURCE),
816 POWERPC_NATIVE_OP_INIT(1, NATIVE_GET_IND_RESOURCE),
817 POWERPC_NATIVE_OP_INIT(1, NATIVE_GET_1_IND_RESOURCE),
818 POWERPC_NATIVE_OP_INIT(1, NATIVE_R_GET_RESOURCE),
819 POWERPC_NATIVE_OP_INIT(0, NATIVE_DISABLE_INTERRUPT),
820 POWERPC_NATIVE_OP_INIT(0, NATIVE_ENABLE_INTERRUPT),
821 POWERPC_NATIVE_OP_INIT(1, NATIVE_MAKE_EXECUTABLE),
822 };
823
824 static void get_resource(void);
825 static void get_1_resource(void);
826 static void get_ind_resource(void);
827 static void get_1_ind_resource(void);
828 static void r_get_resource(void);
829
830 #define GPR(REG) current_cpu->gpr(REG)
831
832 static void NativeOp(int selector)
833 {
834 #if EMUL_TIME_STATS
835 native_exec_count++;
836 const clock_t native_exec_start = clock();
837 #endif
838
839 switch (selector) {
840 case NATIVE_PATCH_NAME_REGISTRY:
841 DoPatchNameRegistry();
842 break;
843 case NATIVE_VIDEO_INSTALL_ACCEL:
844 VideoInstallAccel();
845 break;
846 case NATIVE_VIDEO_VBL:
847 VideoVBL();
848 break;
849 case NATIVE_VIDEO_DO_DRIVER_IO:
850 GPR(3) = (int32)(int16)VideoDoDriverIO((void *)GPR(3), (void *)GPR(4),
851 (void *)GPR(5), GPR(6), GPR(7));
852 break;
853 #ifdef WORDS_BIGENDIAN
854 case NATIVE_ETHER_IRQ:
855 EtherIRQ();
856 break;
857 case NATIVE_ETHER_INIT:
858 GPR(3) = InitStreamModule((void *)GPR(3));
859 break;
860 case NATIVE_ETHER_TERM:
861 TerminateStreamModule();
862 break;
863 case NATIVE_ETHER_OPEN:
864 GPR(3) = ether_open((queue_t *)GPR(3), (void *)GPR(4), GPR(5), GPR(6), (void*)GPR(7));
865 break;
866 case NATIVE_ETHER_CLOSE:
867 GPR(3) = ether_close((queue_t *)GPR(3), GPR(4), (void *)GPR(5));
868 break;
869 case NATIVE_ETHER_WPUT:
870 GPR(3) = ether_wput((queue_t *)GPR(3), (mblk_t *)GPR(4));
871 break;
872 case NATIVE_ETHER_RSRV:
873 GPR(3) = ether_rsrv((queue_t *)GPR(3));
874 break;
875 #else
876 case NATIVE_ETHER_INIT:
877 // FIXME: needs more complicated thunks
878 GPR(3) = false;
879 break;
880 #endif
881 case NATIVE_SERIAL_NOTHING:
882 case NATIVE_SERIAL_OPEN:
883 case NATIVE_SERIAL_PRIME_IN:
884 case NATIVE_SERIAL_PRIME_OUT:
885 case NATIVE_SERIAL_CONTROL:
886 case NATIVE_SERIAL_STATUS:
887 case NATIVE_SERIAL_CLOSE: {
888 typedef int16 (*SerialCallback)(uint32, uint32);
889 static const SerialCallback serial_callbacks[] = {
890 SerialNothing,
891 SerialOpen,
892 SerialPrimeIn,
893 SerialPrimeOut,
894 SerialControl,
895 SerialStatus,
896 SerialClose
897 };
898 GPR(3) = serial_callbacks[selector - NATIVE_SERIAL_NOTHING](GPR(3), GPR(4));
899 break;
900 }
901 case NATIVE_GET_RESOURCE:
902 case NATIVE_GET_1_RESOURCE:
903 case NATIVE_GET_IND_RESOURCE:
904 case NATIVE_GET_1_IND_RESOURCE:
905 case NATIVE_R_GET_RESOURCE: {
906 typedef void (*GetResourceCallback)(void);
907 static const GetResourceCallback get_resource_callbacks[] = {
908 get_resource,
909 get_1_resource,
910 get_ind_resource,
911 get_1_ind_resource,
912 r_get_resource
913 };
914 get_resource_callbacks[selector - NATIVE_GET_RESOURCE]();
915 break;
916 }
917 case NATIVE_DISABLE_INTERRUPT:
918 DisableInterrupt();
919 break;
920 case NATIVE_ENABLE_INTERRUPT:
921 EnableInterrupt();
922 break;
923 case NATIVE_MAKE_EXECUTABLE:
924 MakeExecutable(0, (void *)GPR(4), GPR(5));
925 break;
926 default:
927 printf("FATAL: NATIVE_OP called with bogus selector %d\n", selector);
928 QuitEmulator();
929 break;
930 }
931
932 #if EMUL_TIME_STATS
933 native_exec_time += (clock() - native_exec_start);
934 #endif
935 }
936
937 /*
938 * Execute native subroutine (LR must contain return address)
939 */
940
941 void ExecuteNative(int selector)
942 {
943 uint32 tvect[2];
944 tvect[0] = tswap32(POWERPC_NATIVE_OP_FUNC(selector));
945 tvect[1] = 0; // Fake TVECT
946 RoutineDescriptor desc = BUILD_PPC_ROUTINE_DESCRIPTOR(0, tvect);
947 M68kRegisters r;
948 Execute68k((uint32)&desc, &r);
949 }
950
951 /*
952 * Execute 68k subroutine (must be ended with EXEC_RETURN)
953 * This must only be called by the emul_thread when in EMUL_OP mode
954 * r->a[7] is unused, the routine runs on the caller's stack
955 */
956
957 void Execute68k(uint32 pc, M68kRegisters *r)
958 {
959 current_cpu->execute_68k(pc, r);
960 }
961
962 /*
963 * Execute 68k A-Trap from EMUL_OP routine
964 * r->a[7] is unused, the routine runs on the caller's stack
965 */
966
967 void Execute68kTrap(uint16 trap, M68kRegisters *r)
968 {
969 uint16 proc[2];
970 proc[0] = htons(trap);
971 proc[1] = htons(M68K_RTS);
972 Execute68k((uint32)proc, r);
973 }
974
975 /*
976 * Call MacOS PPC code
977 */
978
979 uint32 call_macos(uint32 tvect)
980 {
981 return current_cpu->execute_macos_code(tvect, 0, NULL);
982 }
983
984 uint32 call_macos1(uint32 tvect, uint32 arg1)
985 {
986 const uint32 args[] = { arg1 };
987 return current_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args);
988 }
989
990 uint32 call_macos2(uint32 tvect, uint32 arg1, uint32 arg2)
991 {
992 const uint32 args[] = { arg1, arg2 };
993 return current_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args);
994 }
995
996 uint32 call_macos3(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3)
997 {
998 const uint32 args[] = { arg1, arg2, arg3 };
999 return current_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args);
1000 }
1001
1002 uint32 call_macos4(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4)
1003 {
1004 const uint32 args[] = { arg1, arg2, arg3, arg4 };
1005 return current_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args);
1006 }
1007
1008 uint32 call_macos5(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4, uint32 arg5)
1009 {
1010 const uint32 args[] = { arg1, arg2, arg3, arg4, arg5 };
1011 return current_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args);
1012 }
1013
1014 uint32 call_macos6(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4, uint32 arg5, uint32 arg6)
1015 {
1016 const uint32 args[] = { arg1, arg2, arg3, arg4, arg5, arg6 };
1017 return current_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args);
1018 }
1019
1020 uint32 call_macos7(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4, uint32 arg5, uint32 arg6, uint32 arg7)
1021 {
1022 const uint32 args[] = { arg1, arg2, arg3, arg4, arg5, arg6, arg7 };
1023 return current_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args);
1024 }
1025
1026 /*
1027 * Resource Manager thunks
1028 */
1029
1030 void get_resource(void)
1031 {
1032 current_cpu->get_resource(ReadMacInt32(XLM_GET_RESOURCE));
1033 }
1034
1035 void get_1_resource(void)
1036 {
1037 current_cpu->get_resource(ReadMacInt32(XLM_GET_1_RESOURCE));
1038 }
1039
1040 void get_ind_resource(void)
1041 {
1042 current_cpu->get_resource(ReadMacInt32(XLM_GET_IND_RESOURCE));
1043 }
1044
1045 void get_1_ind_resource(void)
1046 {
1047 current_cpu->get_resource(ReadMacInt32(XLM_GET_1_IND_RESOURCE));
1048 }
1049
1050 void r_get_resource(void)
1051 {
1052 current_cpu->get_resource(ReadMacInt32(XLM_R_GET_RESOURCE));
1053 }