|
|
1.1 root 1: /*
2: * E-UAE - The portable Amiga Emulator
3: *
4: * Support for traps
5: *
6: * Copyright Richard Drummond 2005
7: *
8: * Inspired by code from UAE:
9: * Copyright 1995, 1996 Bernd Schmidt
10: * Copyright 1996 Ed Hanway
11: */
12:
13: #include "sysconfig.h"
14: #include "sysdeps.h"
15:
16: #include "options.h"
17: #include "memory.h"
18: #include "custom.h"
19: #include "newcpu.h"
20: #include "threaddep/thread.h"
21: #include "autoconf.h"
22: #include "traps.h"
23:
1.1.1.2 ! root 24: /*
! 25: * Traps are the mechanism via which 68k code can call emulator code
! 26: * (and for that emulator code in turn to call 68k code). They are
! 27: * thus the basis for much of the cool stuff that E-UAE can do.
! 28: *
! 29: * Emulator traps take advantage of the illegal 68k opwords 0xA000 to
! 30: * 0xAFFF. Normally these would generate an A-line exception. However,
! 31: * when encountered in the RTAREA section of memory, these opwords
! 32: * instead invoke a corresponding emulator trap, allowing a host
! 33: * function to be called.
! 34: *
! 35: * Two types of emulator trap are available - a simple trap and an
! 36: * extended trap. A simple trap may not call 68k code; an extended
! 37: * trap can.
! 38: *
! 39: * Extended traps are rather complex beasts (to implement, not
! 40: * necessarily to use). This is because for the trap handler function
! 41: * to be able to call 68k code, we must somehow allow the emulator's
! 42: * 68k interpreter to resume execution of 68k code in the middle of
! 43: * the trap handler function.
! 44: *
! 45: * In UAE of old this used to be implemented via a stack-swap mechanism.
! 46: * While this worked, it was definitely in the realm of black magic and
! 47: * horribly non-portable, requiring assembly language glue specific to
! 48: * the host ABI and compiler to actually perform the swap.
! 49: *
! 50: * In this implementation, in essence we do something similar - but the
! 51: * new stack is provided by a new thread. No voodoo required, just a
! 52: * working thread layer.
! 53: *
! 54: * The complexity in this approach arises in synchronizing the trap
! 55: * threads with the emulator thread. This implementation errs on the side
! 56: * of paranoia when it comes to thread synchronization. Once all the
! 57: * bugs are knocked out of the bsdsocket emulation, a simpler scheme may
! 58: * suffice.
! 59: */
! 60:
! 61: /*
! 62: * Record of a defined trap (that is, a trap allocated to a host function)
! 63: */
! 64: struct Trap
! 65: {
! 66: /* Handler function to be invoked for this trap. */
! 67: TrapHandler handler;
! 68: /* Trap attributes. */
! 69: int flags;
! 70: /* For debugging purposes. */
! 71: const char *name;
! 72: };
! 73:
1.1 root 74: #define MAX_TRAPS 4096
75:
1.1.1.2 ! root 76: /* Defined traps */
! 77: static struct Trap traps[MAX_TRAPS];
! 78: static unsigned int trap_count;
! 79:
! 80:
! 81: static const int trace_traps = 1;
! 82:
1.1 root 83:
1.1.1.2 ! root 84: static void trap_HandleExtendedTrap (TrapHandler, int has_retval);
1.1 root 85:
1.1.1.2 ! root 86: /*
! 87: * Define an emulator trap
1.1 root 88: *
1.1.1.2 ! root 89: * handler_func = host function that will be invoked to handle this trap
! 90: * flags = trap attributes
! 91: * name = name for debugging purposes
1.1 root 92: *
1.1.1.2 ! root 93: * returns trap number of defined trap
1.1 root 94: */
1.1.1.2 ! root 95: unsigned int define_trap (TrapHandler handler_func, int flags, const char *name)
! 96: {
! 97: if (trap_count == MAX_TRAPS) {
! 98: write_log ("Ran out of emulator traps\n");
! 99: abort ();
! 100: } else {
! 101: unsigned int trap_num = trap_count++;
! 102: struct Trap *trap = &traps[trap_num];
! 103:
! 104: trap->handler = handler_func;
! 105: trap->flags = flags;
! 106: trap->name = name;
1.1 root 107:
1.1.1.2 ! root 108: return trap_num;
! 109: }
! 110: }
1.1 root 111:
112:
1.1.1.2 ! root 113: /*
! 114: * This function is called by the 68k interpreter to handle an emulator trap.
! 115: *
! 116: * trap_num = number of trap to invoke
! 117: * regs = current 68k state
! 118: */
! 119: void m68k_handle_trap (unsigned int trap_num)
1.1 root 120: {
1.1.1.2 ! root 121: struct Trap *trap = &traps[trap_num];
! 122: uae_u32 retval = 0;
! 123:
! 124: int has_retval = (trap->flags & TRAPFLAG_NO_RETVAL) == 0;
! 125: int implicit_rts = (trap->flags & TRAPFLAG_DORET) != 0;
! 126:
! 127: if (trap->name && trap->name[0] != 0 && trace_traps)
! 128: write_log ("TRAP: %s\n", trap->name);
! 129:
! 130: if (trap_num < trap_count) {
! 131: if (trap->flags & TRAPFLAG_EXTRA_STACK) {
! 132: /* Handle an extended trap.
! 133: * Note: the return value of this trap is passed back to 68k
! 134: * space via a separate, dedicated simple trap which the trap
! 135: * handler causes to be invoked when it is done.
! 136: */
! 137: trap_HandleExtendedTrap (trap->handler, has_retval);
! 138: } else {
! 139: /* Handle simple trap */
! 140: retval = (trap->handler) (NULL);
! 141:
! 142: if (has_retval)
! 143: m68k_dreg (regs, 0) = retval;
! 144:
! 145: if (implicit_rts) {
! 146: m68k_do_rts ();
! 147: fill_prefetch_slow ();
! 148: }
! 149: }
! 150: } else
! 151: write_log ("Illegal emulator trap\n");
1.1 root 152: }
153:
1.1.1.2 ! root 154:
! 155:
! 156: /*
! 157: * Implementation of extended traps
! 158: */
! 159:
! 160: struct TrapContext
1.1 root 161: {
1.1.1.2 ! root 162: /* Trap's working copy of 68k state. This is what the trap handler should
! 163: * access to get arguments from 68k space. */
! 164: struct regstruct regs;
! 165:
! 166: /* Trap handler function that gets called on the trap context */
! 167: TrapHandler trap_handler;
! 168: /* Should the handler return a value to 68k space in D0? */
! 169: int trap_has_retval;
! 170: /* Return value from trap handler */
! 171: uae_u32 trap_retval;
! 172:
! 173: /* Copy of 68k state at trap entry. */
! 174: struct regstruct saved_regs;
! 175:
! 176: /* Thread which effects the trap context. */
! 177: uae_thread_id thread;
! 178: /* For IPC between the main emulator. */
! 179: uae_sem_t switch_to_emu_sem;
! 180: /* context and the trap context. */
! 181: uae_sem_t switch_to_trap_sem;
! 182:
! 183: /* When calling a 68k function from a trap handler, this is set to the
! 184: * address of the function to call. */
! 185: uaecptr call68k_func_addr;
! 186: /* And this gets set to the return value of the 68k call. */
! 187: uae_u32 call68k_retval;
! 188: };
! 189:
! 190:
! 191: /* 68k addresses which invoke the corresponding traps. */
! 192: static uaecptr m68k_call_trapaddr;
! 193: static uaecptr m68k_return_trapaddr;
! 194: static uaecptr exit_trap_trapaddr;
! 195:
! 196: /* For IPC between main thread and trap context */
! 197: static uae_sem_t trap_mutex;
! 198: static TrapContext *current_context;
! 199:
1.1 root 200:
1.1.1.2 ! root 201: /*
! 202: * Thread body for trap context
! 203: */
! 204: static void *trap_thread (void *arg)
1.1 root 205: {
1.1.1.2 ! root 206: TrapContext *context = (TrapContext *) arg;
! 207:
! 208: /* Wait until main thread is ready to switch to the
! 209: * this trap context. */
! 210: uae_sem_wait (&context->switch_to_trap_sem);
1.1 root 211:
1.1.1.2 ! root 212: /* Execute trap handler function. */
! 213: context->trap_retval = context->trap_handler (context);
1.1 root 214:
1.1.1.2 ! root 215: /* Trap handler is done - we still need to tidy up
! 216: * and make sure the handler's return value is propagated
! 217: * to the calling 68k thread.
! 218: *
! 219: * We do this by causing our exit handler to be executed on the 68k context.
! 220: */
! 221:
! 222: /* Enter critical section - only one trap at a time, please! */
! 223: uae_sem_wait (&trap_mutex);
! 224:
! 225: regs = context->saved_regs;
! 226: /* Don't allow an interrupt and thus potentially another
! 227: * trap to be invoked while we hold the above mutex.
! 228: * This is probably just being paranoid. */
! 229: regs.intmask = 7;
! 230:
! 231: /* Set PC to address of the exit handler, so that it will be called
! 232: * when the 68k context resumes. */
! 233: m68k_setpc (exit_trap_trapaddr);
! 234: current_context = context;
! 235:
! 236: /* Switch back to 68k context */
! 237: uae_sem_post (&context->switch_to_emu_sem);
! 238:
! 239: /* Good bye, cruel world... */
! 240:
! 241: /* dummy return value */
! 242: return 0;
! 243: }
! 244:
! 245: /*
! 246: * Set up extended trap context and call handler function
! 247: */
! 248: static void trap_HandleExtendedTrap (TrapHandler handler_func, int has_retval)
1.1 root 249: {
1.1.1.2 ! root 250: struct TrapContext *context = calloc (1, sizeof (TrapContext));
! 251:
! 252: if (context) {
! 253: uae_sem_init (&context->switch_to_trap_sem, 0, 0);
! 254: uae_sem_init (&context->switch_to_emu_sem, 0, 0);
! 255:
! 256: context->trap_handler = handler_func;
! 257: context->trap_has_retval = has_retval;
! 258:
! 259: context->saved_regs = regs;
1.1 root 260:
1.1.1.2 ! root 261: /* Start thread to handle new trap context. */
! 262: uae_start_thread (trap_thread, (void *)context, &context->thread);
! 263:
! 264: /* Switch to trap context to begin execution of
! 265: * trap handler function.
! 266: */
! 267: uae_sem_post (&context->switch_to_trap_sem);
! 268:
! 269: /* Wait for trap context to switch back to us.
! 270: *
! 271: * It'll do this when the trap handler is done - or when
! 272: * the handler wants to call 68k code. */
! 273: uae_sem_wait (&context->switch_to_emu_sem);
1.1 root 274: }
275: }
276:
1.1.1.2 ! root 277: /*
! 278: * Call m68k function from an extended trap handler
! 279: *
! 280: * This function is to be called from the trap context.
! 281: */
! 282: static uae_u32 trap_Call68k (TrapContext *context, uaecptr func_addr)
1.1 root 283: {
1.1.1.2 ! root 284: /* Enter critical section - only one trap at a time, please! */
! 285: uae_sem_wait (&trap_mutex);
! 286: current_context = context;
! 287:
! 288: /* Don't allow an interrupt and thus potentially another
! 289: * trap to be invoked while we hold the above mutex.
! 290: * This is probably just being paranoid. */
! 291: regs.intmask = 7;
! 292:
! 293: /* Set up function call address. */
! 294: context->call68k_func_addr = func_addr;
! 295:
! 296: /* Set PC to address of 68k call trap, so that it will be
! 297: * executed when emulator context resumes. */
! 298: m68k_setpc (m68k_call_trapaddr);
! 299: fill_prefetch_slow ();
! 300:
! 301: /* Switch to emulator context. */
! 302: uae_sem_post (&context->switch_to_emu_sem);
! 303:
! 304: /* Wait for 68k call return handler to switch back to us. */
! 305: uae_sem_wait (&context->switch_to_trap_sem);
! 306:
! 307: /* End critical section. */
! 308: uae_sem_post (&trap_mutex);
! 309:
! 310: /* Get return value from 68k function called. */
! 311: return context->call68k_retval;
1.1 root 312: }
313:
1.1.1.2 ! root 314: /*
! 315: * Handles the emulator's side of a 68k call (from an extended trap)
! 316: */
! 317: static uae_u32 m68k_call_handler (TrapContext *dummy_ctx)
1.1 root 318: {
1.1.1.2 ! root 319: TrapContext *context = current_context;
! 320:
! 321: uae_u32 sp;
! 322:
! 323: sp = m68k_areg (regs, 7);
! 324:
! 325: /* Push address of trap context on 68k stack. This is
! 326: * so the return trap can find this context. */
! 327: sp -= sizeof (void *);
! 328: put_pointer (sp, context);
! 329:
! 330: /* Push addr to return handler trap on 68k stack.
! 331: * When the called m68k function does an RTS, the CPU will pull this
! 332: * address off the stack and so call the return handler. */
! 333: sp -= 4;
! 334: put_long (sp, m68k_return_trapaddr);
! 335:
! 336: m68k_areg (regs, 7) = sp;
! 337:
! 338: /* Set PC to address of 68k function to call. */
! 339: m68k_setpc (context->call68k_func_addr);
! 340: fill_prefetch_slow ();
! 341:
! 342: /* End critical section: allow other traps run. */
! 343: uae_sem_post (&trap_mutex);
! 344:
! 345: /* Restore interrupts. */
! 346: regs.intmask = context->saved_regs.intmask;
! 347:
! 348: /* Dummy return value. */
1.1 root 349: return 0;
350: }
351:
1.1.1.2 ! root 352: /*
! 353: * Handles the return from a 68k call at the emulator's side.
! 354: */
! 355: static uae_u32 m68k_return_handler (TrapContext *dummy_ctx)
1.1 root 356: {
1.1.1.2 ! root 357: TrapContext *context;
! 358: uae_u32 sp;
! 359:
! 360: /* One trap returning at a time, please! */
! 361: uae_sem_wait (&trap_mutex);
! 362:
! 363: /* Get trap context from 68k stack. */
! 364: sp = m68k_areg (regs, 7);
! 365: context = (TrapContext *)get_pointer (sp);
! 366: sp += sizeof (void *);
! 367: m68k_areg (regs, 7) = sp;
! 368:
! 369: /* Get return value from the 68k call. */
! 370: context->call68k_retval = m68k_dreg (regs, 0);
! 371:
! 372: /* Switch back to trap context. */
! 373: uae_sem_post (&context->switch_to_trap_sem);
! 374:
! 375: /* Wait for trap context to switch back to us.
! 376: *
! 377: * It'll do this when the trap handler is done - or when
! 378: * the handler wants to call another 68k function. */
! 379: uae_sem_wait (&context->switch_to_emu_sem);
! 380:
! 381: /* Dummy return value. */
! 382: return 0;
1.1 root 383: }
384:
1.1.1.2 ! root 385: /*
! 386: * Handles completion of an extended trap and passes
! 387: * return value from trap function to 68k space.
! 388: */
! 389: static uae_u32 exit_trap_handler (TrapContext *dummy_ctx)
1.1 root 390: {
1.1.1.2 ! root 391: TrapContext *context = current_context;
1.1 root 392:
1.1.1.2 ! root 393: /* Wait for trap context thread to exit. */
! 394: uae_wait_thread (context->thread);
1.1 root 395:
1.1.1.2 ! root 396: /* Restore 68k state saved at trap entry. */
! 397: regs = context->saved_regs;
1.1 root 398:
1.1.1.2 ! root 399: /* If trap is supposed to return a value, then store
! 400: * return value in D0. */
! 401: if (context->trap_has_retval)
! 402: m68k_dreg (regs, 0) = context->trap_retval;
1.1 root 403:
1.1.1.2 ! root 404: uae_sem_destroy (&context->switch_to_trap_sem);
! 405: uae_sem_destroy (&context->switch_to_emu_sem);
1.1 root 406:
1.1.1.2 ! root 407: free (context);
1.1 root 408:
1.1.1.2 ! root 409: /* End critical section */
! 410: uae_sem_post (&trap_mutex);
! 411:
! 412: /* Dummy return value. */
! 413: return 0;
1.1 root 414: }
415:
1.1.1.2 ! root 416:
! 417:
! 418: /*
! 419: * Call a 68k library function from extended trap.
! 420: */
! 421: uae_u32 CallLib (TrapContext *context, uaecptr base, uae_s16 offset)
! 422: {
! 423: uae_u32 retval;
! 424: uaecptr olda6 = m68k_areg (regs, 6);
! 425:
! 426: m68k_areg (regs, 6) = base;
! 427: retval = trap_Call68k (context, base + offset);
! 428: m68k_areg (regs, 6) = olda6;
! 429:
1.1 root 430: return retval;
431: }
432:
1.1.1.2 ! root 433:
! 434: /*
! 435: * Initialize trap mechanism.
! 436: */
! 437: void init_traps (void)
1.1 root 438: {
1.1.1.2 ! root 439: trap_count = 0;
1.1 root 440: }
441:
1.1.1.2 ! root 442: /*
! 443: * Initialize the extended trap mechanism.
! 444: */
! 445: void init_extended_traps (void)
1.1 root 446: {
1.1.1.2 ! root 447: m68k_call_trapaddr = here ();
! 448: calltrap (deftrap2 (m68k_call_handler, TRAPFLAG_NO_RETVAL, "m68k_call"));
! 449:
! 450: m68k_return_trapaddr = here();
! 451: calltrap (deftrap2 (m68k_return_handler, TRAPFLAG_NO_RETVAL, "m68k_return"));
! 452:
! 453: exit_trap_trapaddr = here();
! 454: calltrap (deftrap2 (exit_trap_handler, TRAPFLAG_NO_RETVAL, "exit_trap"));
! 455:
! 456: uae_sem_init (&trap_mutex, 0, 1);
1.1 root 457: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.