Annotation of uae/src/traps.c, revision 1.1.1.2

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.