syn68k/runtime/callback.c
2008-09-26 08:25:10 -06:00

187 lines
4.7 KiB
C

#include "callback.h"
#include "rangetree.h"
#include "block.h"
#include "alloc.h"
#include "hash.h"
#include "destroyblock.h"
#include "deathqueue.h"
#include "checksum.h"
#include "translate.h"
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct {
callback_handler_t func;
void *arg;
} CallBackInfo;
CallBackInfo *callback;
int num_callback_slots;
int lowest_free_callback_slot;
/* We just use this array to reserve some dereferenceable address
* space to hold the callbacks, because we need memory locations we
* know can never contain 68k code. We don't actually store anything
* there.
*/
uint16 callback_dummy_address_space[MAX_CALLBACKS + CALLBACK_SLOP];
void
callback_init (void)
{
callback = NULL;
num_callback_slots = 0;
lowest_free_callback_slot = 0;
}
/* Installs a given callback function at a given 68k address. If the 68k
* ever executes code at that address, the callback function will be called
* and provided with the 68k address of the callback and an arbitrary
* 32 bit argument. If some block is already present at the specified
* address, does nothing and returns 0. Otherwise, returns 1.
*/
int
callback_compile (Block *parent, syn68k_addr_t m68k_address, Block **new)
{
Block *b;
uint16 *code;
uint32 ix = (m68k_address - CALLBACK_STUB_BASE) / sizeof (uint16);
const CallBackInfo *callback_info = &callback[ix];
/* Make sure a callback is actually installed. */
if (ix >= num_callback_slots || callback_info->func == NULL)
{
*new = NULL;
return 0;
}
b = *new = make_artificial_block (parent, m68k_address,
OPCODE_WORDS + PTR_WORDS + PTR_WORDS + 2,
&code);
/* Create the synthetic code for the callback. */
#ifdef USE_DIRECT_DISPATCH
*(const void **)code = direct_dispatch_table[0xB3]; /* callback opcode. */
#else
*(void **)code = (void *)0xB3; /* Magical callback synthetic opcode. */
#endif
*((callback_handler_t *) (code + OPCODE_WORDS)) = callback_info->func;
*(void **)(code + OPCODE_WORDS + PTR_WORDS) = callback_info->arg;
*(syn68k_addr_t *)(code + OPCODE_WORDS + PTR_WORDS + PTR_WORDS)
= m68k_address;
/* Insert block into the universe of blocks. */
hash_insert (b);
range_tree_insert (b);
/* Add this block to the end of the death queue. */
death_queue_enqueue (b);
b->immortal = FALSE;
return ALL_CCS;
}
/* Installs a callback stub in the 68k space and returns the 68k address
* it chose for this stub. Any 68k code that hits this stub will call
* the specified callback function. func must never be NULL.
*/
syn68k_addr_t
callback_install (callback_handler_t func, void *arbitrary_argument)
{
uint32 slot;
int old_sigmask;
BLOCK_INTERRUPTS (old_sigmask);
slot = lowest_free_callback_slot;
if (lowest_free_callback_slot >= num_callback_slots)
{
if (num_callback_slots >= MAX_CALLBACKS)
{
fprintf (stderr, "Internal error: syn68k out of callback slots!\n");
abort ();
}
++num_callback_slots;
callback = (CallBackInfo *) xrealloc (callback, (num_callback_slots
* sizeof callback[0]));
}
/* Remember the callback they are specifying. */
callback[slot].func = func;
callback[slot].arg = arbitrary_argument;
/* Move lowest free callback slot to next lowest slot. */
for (lowest_free_callback_slot++; ; lowest_free_callback_slot++)
if (lowest_free_callback_slot >= num_callback_slots
|| callback[lowest_free_callback_slot].func == NULL)
break;
/* Reenable interrupts. */
RESTORE_INTERRUPTS (old_sigmask);
return CALLBACK_STUB_BASE + (slot * sizeof (uint16));
}
void *
callback_argument (syn68k_addr_t callback_address)
{
uint32 ix = (callback_address - CALLBACK_STUB_BASE) / sizeof (uint16);
if (ix >= num_callback_slots)
return NULL;
return callback[ix].arg;
}
callback_handler_t
callback_function (syn68k_addr_t callback_address)
{
uint32 ix = (callback_address - CALLBACK_STUB_BASE) / sizeof (uint16);
if (ix >= num_callback_slots)
return NULL;
return callback[ix].func;
}
void
callback_remove (syn68k_addr_t m68k_address)
{
uint32 ix;
Block *b;
int old_sigmask;
BLOCK_INTERRUPTS (old_sigmask);
/* Fetch the block at that address. */
b = hash_lookup (m68k_address);
if (b != NULL)
destroy_block (b);
ix = (m68k_address - CALLBACK_STUB_BASE) / sizeof (uint16);
if (ix < num_callback_slots)
{
if (ix == num_callback_slots - 1)
{
--num_callback_slots;
callback = (CallBackInfo *) xrealloc (callback,
(num_callback_slots
* sizeof callback[0]));
}
else
{
callback[ix].func = NULL;
if (ix < lowest_free_callback_slot)
lowest_free_callback_slot = ix;
}
}
RESTORE_INTERRUPTS (old_sigmask);
}