856 lines
21 KiB
C++
856 lines
21 KiB
C++
/*
|
|
* E-UAE - The portable Amiga Emulator
|
|
*
|
|
* Support for traps
|
|
*
|
|
* Copyright Richard Drummond 2005
|
|
*
|
|
* Inspired by code from UAE:
|
|
* Copyright 1995, 1996 Bernd Schmidt
|
|
* Copyright 1996 Ed Hanway
|
|
*/
|
|
|
|
#include "sysconfig.h"
|
|
#include "sysdeps.h"
|
|
|
|
#include "options.h"
|
|
#include "include/memory.h"
|
|
#include "custom.h"
|
|
#include "newcpu.h"
|
|
#include "threaddep/thread.h"
|
|
#include "autoconf.h"
|
|
#include "traps.h"
|
|
#include "uae.h"
|
|
|
|
/*
|
|
* Traps are the mechanism via which 68k code can call emulator code
|
|
* (and for that emulator code in turn to call 68k code). They are
|
|
* thus the basis for much of the cool stuff that E-UAE can do.
|
|
*
|
|
* Emulator traps take advantage of the illegal 68k opwords 0xA000 to
|
|
* 0xAFFF. Normally these would generate an A-line exception. However,
|
|
* when encountered in the RTAREA section of memory, these opwords
|
|
* instead invoke a corresponding emulator trap, allowing a host
|
|
* function to be called.
|
|
*
|
|
* Two types of emulator trap are available - a simple trap and an
|
|
* extended trap. A simple trap may not call 68k code; an extended
|
|
* trap can.
|
|
*
|
|
* Extended traps are rather complex beasts (to implement, not
|
|
* necessarily to use). This is because for the trap handler function
|
|
* to be able to call 68k code, we must somehow allow the emulator's
|
|
* 68k interpreter to resume execution of 68k code in the middle of
|
|
* the trap handler function.
|
|
*
|
|
* In UAE of old this used to be implemented via a stack-swap mechanism.
|
|
* While this worked, it was definitely in the realm of black magic and
|
|
* horribly non-portable, requiring assembly language glue specific to
|
|
* the host ABI and compiler to actually perform the swap.
|
|
*
|
|
* In this implementation, in essence we do something similar - but the
|
|
* new stack is provided by a new thread. No voodoo required, just a
|
|
* working thread layer.
|
|
*
|
|
* The complexity in this approach arises in synchronizing the trap
|
|
* threads with the emulator thread. This implementation errs on the side
|
|
* of paranoia when it comes to thread synchronization. Once all the
|
|
* bugs are knocked out of the bsdsocket emulation, a simpler scheme may
|
|
* suffice.
|
|
*/
|
|
|
|
/*
|
|
* Record of a defined trap (that is, a trap allocated to a host function)
|
|
*/
|
|
struct Trap
|
|
{
|
|
TrapHandler handler; /* Handler function to be invoked for this trap. */
|
|
int flags; /* Trap attributes. */
|
|
const TCHAR *name; /* For debugging purposes. */
|
|
uaecptr addr;
|
|
};
|
|
|
|
#define MAX_TRAPS 4096
|
|
|
|
/* Defined traps */
|
|
static struct Trap traps[MAX_TRAPS];
|
|
static unsigned int trap_count = 1;
|
|
|
|
static void trap_HandleExtendedTrap (TrapHandler, int has_retval);
|
|
|
|
uaecptr find_trap (const TCHAR *name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < trap_count; i++) {
|
|
struct Trap *trap = &traps[i];
|
|
if ((trap->flags & TRAPFLAG_UAERES) && trap->name && !_tcscmp (trap->name, name))
|
|
return trap->addr;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Define an emulator trap
|
|
*
|
|
* handler_func = host function that will be invoked to handle this trap
|
|
* flags = trap attributes
|
|
* name = name for debugging purposes
|
|
*
|
|
* returns trap number of defined trap
|
|
*/
|
|
unsigned int define_trap (TrapHandler handler_func, int flags, const TCHAR *name)
|
|
{
|
|
if (trap_count == MAX_TRAPS) {
|
|
write_log (_T("Ran out of emulator traps\n"));
|
|
target_startup_msg(_T("Internal error"), _T("Ran out of emulator traps."));
|
|
uae_restart(1, NULL);
|
|
return -1;
|
|
} else {
|
|
int i;
|
|
unsigned int trap_num;
|
|
struct Trap *trap;
|
|
uaecptr addr = here ();
|
|
|
|
for (i = 0; i < trap_count; i++) {
|
|
if (addr == traps[i].addr)
|
|
return i;
|
|
}
|
|
|
|
trap_num = trap_count++;
|
|
trap = &traps[trap_num];
|
|
|
|
trap->handler = handler_func;
|
|
trap->flags = flags;
|
|
trap->name = name;
|
|
trap->addr = addr;
|
|
|
|
return trap_num;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* This function is called by the 68k interpreter to handle an emulator trap.
|
|
*
|
|
* trap_num = number of trap to invoke
|
|
* regs = current 68k state
|
|
*/
|
|
void REGPARAM2 m68k_handle_trap (unsigned int trap_num)
|
|
{
|
|
struct Trap *trap = &traps[trap_num];
|
|
uae_u32 retval = 0;
|
|
|
|
int has_retval = (trap->flags & TRAPFLAG_NO_RETVAL) == 0;
|
|
int implicit_rts = (trap->flags & TRAPFLAG_DORET) != 0;
|
|
|
|
if (trap_num < trap_count) {
|
|
if (trap->flags & TRAPFLAG_EXTRA_STACK) {
|
|
/* Handle an extended trap.
|
|
* Note: the return value of this trap is passed back to 68k
|
|
* space via a separate, dedicated simple trap which the trap
|
|
* handler causes to be invoked when it is done.
|
|
*/
|
|
trap_HandleExtendedTrap (trap->handler, has_retval);
|
|
} else {
|
|
/* Handle simple trap */
|
|
retval = (trap->handler) (NULL);
|
|
|
|
if (has_retval)
|
|
m68k_dreg (regs, 0) = retval;
|
|
|
|
if (implicit_rts) {
|
|
m68k_do_rts ();
|
|
fill_prefetch ();
|
|
}
|
|
}
|
|
} else
|
|
write_log (_T("Illegal emulator trap\n"));
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Implementation of extended traps
|
|
*/
|
|
|
|
struct TrapCPUContext
|
|
{
|
|
uae_u32 regs[16];
|
|
uae_u32 pc;
|
|
int intmask;
|
|
};
|
|
|
|
struct TrapContext
|
|
{
|
|
/* Trap's working copy of 68k state. This is what the trap handler should
|
|
* access to get arguments from 68k space. */
|
|
|
|
/* Trap handler function that gets called on the trap context */
|
|
TrapHandler trap_handler;
|
|
/* Should the handler return a value to 68k space in D0? */
|
|
int trap_has_retval;
|
|
/* Return value from trap handler */
|
|
uae_u32 trap_retval;
|
|
|
|
/* Copy of 68k state at trap entry. */
|
|
struct TrapCPUContext saved_regs;
|
|
|
|
/* Thread which effects the trap context. */
|
|
uae_thread_id thread;
|
|
/* For IPC between the main emulator. */
|
|
uae_sem_t switch_to_emu_sem;
|
|
/* context and the trap context. */
|
|
uae_sem_t switch_to_trap_sem;
|
|
|
|
/* When calling a 68k function from a trap handler, this is set to the
|
|
* address of the function to call. */
|
|
uaecptr call68k_func_addr;
|
|
/* And this gets set to the return value of the 68k call. */
|
|
uae_u32 call68k_retval;
|
|
|
|
uae_u32 calllib_regs[16];
|
|
uae_u8 calllib_reg_inuse[16];
|
|
};
|
|
|
|
static void copytocpucontext(struct TrapCPUContext *cpu)
|
|
{
|
|
memcpy (cpu->regs, regs.regs, sizeof (regs.regs));
|
|
cpu->intmask = regs.intmask;
|
|
cpu->pc = m68k_getpc ();
|
|
}
|
|
static void copyfromcpucontext(struct TrapCPUContext *cpu, uae_u32 pc)
|
|
{
|
|
memcpy (regs.regs, cpu->regs, sizeof (regs.regs));
|
|
regs.intmask = cpu->intmask;
|
|
m68k_setpc (pc);
|
|
}
|
|
|
|
|
|
/* 68k addresses which invoke the corresponding traps. */
|
|
static uaecptr m68k_call_trapaddr;
|
|
static uaecptr m68k_return_trapaddr;
|
|
static uaecptr exit_trap_trapaddr;
|
|
|
|
/* For IPC between main thread and trap context */
|
|
static uae_sem_t trap_mutex = 0;
|
|
static TrapContext *current_context;
|
|
|
|
|
|
/*
|
|
* Thread body for trap context
|
|
*/
|
|
static void *trap_thread (void *arg)
|
|
{
|
|
TrapContext *context = (TrapContext *) arg;
|
|
|
|
/* Wait until main thread is ready to switch to the
|
|
* this trap context. */
|
|
uae_sem_wait (&context->switch_to_trap_sem);
|
|
|
|
/* Execute trap handler function. */
|
|
context->trap_retval = context->trap_handler (context);
|
|
|
|
/* Trap handler is done - we still need to tidy up
|
|
* and make sure the handler's return value is propagated
|
|
* to the calling 68k thread.
|
|
*
|
|
* We do this by causing our exit handler to be executed on the 68k context.
|
|
*/
|
|
|
|
/* Enter critical section - only one trap at a time, please! */
|
|
uae_sem_wait (&trap_mutex);
|
|
|
|
//regs = context->saved_regs;
|
|
/* Set PC to address of the exit handler, so that it will be called
|
|
* when the 68k context resumes. */
|
|
copyfromcpucontext (&context->saved_regs, exit_trap_trapaddr);
|
|
/* Don't allow an interrupt and thus potentially another
|
|
* trap to be invoked while we hold the above mutex.
|
|
* This is probably just being paranoid. */
|
|
regs.intmask = 7;
|
|
|
|
//m68k_setpc (exit_trap_trapaddr);
|
|
current_context = context;
|
|
|
|
/* Switch back to 68k context */
|
|
uae_sem_post (&context->switch_to_emu_sem);
|
|
|
|
/* Good bye, cruel world... */
|
|
|
|
/* dummy return value */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Set up extended trap context and call handler function
|
|
*/
|
|
static void trap_HandleExtendedTrap (TrapHandler handler_func, int has_retval)
|
|
{
|
|
struct TrapContext *context = xcalloc (TrapContext, 1);
|
|
|
|
if (context) {
|
|
uae_sem_init (&context->switch_to_trap_sem, 0, 0);
|
|
uae_sem_init (&context->switch_to_emu_sem, 0, 0);
|
|
|
|
context->trap_handler = handler_func;
|
|
context->trap_has_retval = has_retval;
|
|
|
|
//context->saved_regs = regs;
|
|
copytocpucontext (&context->saved_regs);
|
|
|
|
/* Start thread to handle new trap context. */
|
|
uae_start_thread_fast (trap_thread, (void *)context, &context->thread);
|
|
|
|
/* Switch to trap context to begin execution of
|
|
* trap handler function.
|
|
*/
|
|
uae_sem_post (&context->switch_to_trap_sem);
|
|
|
|
/* Wait for trap context to switch back to us.
|
|
*
|
|
* It'll do this when the trap handler is done - or when
|
|
* the handler wants to call 68k code. */
|
|
uae_sem_wait (&context->switch_to_emu_sem);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Call m68k function from an extended trap handler
|
|
*
|
|
* This function is to be called from the trap context.
|
|
*/
|
|
|
|
static uae_u32 trap_Call68k (TrapContext *ctx, uaecptr func_addr)
|
|
{
|
|
/* Enter critical section - only one trap at a time, please! */
|
|
uae_sem_wait (&trap_mutex);
|
|
current_context = ctx;
|
|
|
|
/* Don't allow an interrupt and thus potentially another
|
|
* trap to be invoked while we hold the above mutex.
|
|
* This is probably just being paranoid. */
|
|
regs.intmask = 7;
|
|
|
|
/* Set up function call address. */
|
|
ctx->call68k_func_addr = func_addr;
|
|
|
|
/* Set PC to address of 68k call trap, so that it will be
|
|
* executed when emulator context resumes. */
|
|
m68k_setpc (m68k_call_trapaddr);
|
|
fill_prefetch ();
|
|
|
|
/* Switch to emulator context. */
|
|
uae_sem_post (&ctx->switch_to_emu_sem);
|
|
|
|
/* Wait for 68k call return handler to switch back to us. */
|
|
uae_sem_wait (&ctx->switch_to_trap_sem);
|
|
|
|
/* End critical section. */
|
|
uae_sem_post (&trap_mutex);
|
|
|
|
/* Get return value from 68k function called. */
|
|
return ctx->call68k_retval;
|
|
}
|
|
|
|
/*
|
|
* Handles the emulator's side of a 68k call (from an extended trap)
|
|
*/
|
|
static uae_u32 REGPARAM2 m68k_call_handler (TrapContext *dummy_ctx)
|
|
{
|
|
TrapContext *context = current_context;
|
|
|
|
uae_u32 sp;
|
|
|
|
sp = m68k_areg (regs, 7);
|
|
|
|
/* Push address of trap context on 68k stack. This is
|
|
* so the return trap can find this context. */
|
|
sp -= sizeof (void *);
|
|
put_pointer (sp, context);
|
|
|
|
/* Push addr to return handler trap on 68k stack.
|
|
* When the called m68k function does an RTS, the CPU will pull this
|
|
* address off the stack and so call the return handler. */
|
|
sp -= 4;
|
|
put_long (sp, m68k_return_trapaddr);
|
|
|
|
m68k_areg (regs, 7) = sp;
|
|
|
|
/* Set PC to address of 68k function to call. */
|
|
m68k_setpc (context->call68k_func_addr);
|
|
fill_prefetch ();
|
|
|
|
/* End critical section: allow other traps run. */
|
|
uae_sem_post (&trap_mutex);
|
|
|
|
/* Restore interrupts. */
|
|
regs.intmask = context->saved_regs.intmask;
|
|
|
|
/* Dummy return value. */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Handles the return from a 68k call at the emulator's side.
|
|
*/
|
|
static uae_u32 REGPARAM2 m68k_return_handler (TrapContext *dummy_ctx)
|
|
{
|
|
TrapContext *context;
|
|
uae_u32 sp;
|
|
|
|
/* One trap returning at a time, please! */
|
|
uae_sem_wait (&trap_mutex);
|
|
|
|
/* Get trap context from 68k stack. */
|
|
sp = m68k_areg (regs, 7);
|
|
context = (TrapContext *) get_pointer(sp);
|
|
sp += sizeof (void *);
|
|
m68k_areg (regs, 7) = sp;
|
|
|
|
/* Get return value from the 68k call. */
|
|
context->call68k_retval = m68k_dreg (regs, 0);
|
|
|
|
/* Switch back to trap context. */
|
|
uae_sem_post (&context->switch_to_trap_sem);
|
|
|
|
/* Wait for trap context to switch back to us.
|
|
*
|
|
* It'll do this when the trap handler is done - or when
|
|
* the handler wants to call another 68k function. */
|
|
uae_sem_wait (&context->switch_to_emu_sem);
|
|
|
|
/* Dummy return value. */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Handles completion of an extended trap and passes
|
|
* return value from trap function to 68k space.
|
|
*/
|
|
static uae_u32 REGPARAM2 exit_trap_handler (TrapContext *dummy_ctx)
|
|
{
|
|
TrapContext *context = current_context;
|
|
|
|
/* Wait for trap context thread to exit. */
|
|
uae_wait_thread (context->thread);
|
|
|
|
/* Restore 68k state saved at trap entry. */
|
|
//regs = context->saved_regs;
|
|
copyfromcpucontext (&context->saved_regs, context->saved_regs.pc);
|
|
|
|
/* If trap is supposed to return a value, then store
|
|
* return value in D0. */
|
|
if (context->trap_has_retval)
|
|
m68k_dreg (regs, 0) = context->trap_retval;
|
|
|
|
uae_sem_destroy (&context->switch_to_trap_sem);
|
|
uae_sem_destroy (&context->switch_to_emu_sem);
|
|
|
|
xfree (context);
|
|
|
|
/* End critical section */
|
|
uae_sem_post (&trap_mutex);
|
|
|
|
/* Dummy return value. */
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Call a 68k library function from extended trap.
|
|
*/
|
|
uae_u32 CallLib (TrapContext *ctx, uaecptr base, uae_s16 offset)
|
|
{
|
|
uae_u32 retval;
|
|
uaecptr olda6 = trap_get_areg(ctx, 6);
|
|
|
|
trap_set_areg(ctx, 6, base);
|
|
retval = trap_Call68k (ctx, base + offset);
|
|
trap_set_areg(ctx, 6, olda6);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* Call 68k function from extended trap.
|
|
*/
|
|
uae_u32 CallFunc(TrapContext *ctx, uaecptr func)
|
|
{
|
|
return trap_Call68k(ctx, func);
|
|
}
|
|
|
|
|
|
/*
|
|
* Initialize trap mechanism.
|
|
*/
|
|
void init_traps (void)
|
|
{
|
|
trap_count = 0;
|
|
}
|
|
|
|
/*
|
|
* Initialize the extended trap mechanism.
|
|
*/
|
|
void init_extended_traps (void)
|
|
{
|
|
m68k_call_trapaddr = here ();
|
|
calltrap (deftrap2 (m68k_call_handler, TRAPFLAG_NO_RETVAL, _T("m68k_call")));
|
|
|
|
m68k_return_trapaddr = here();
|
|
calltrap (deftrap2 (m68k_return_handler, TRAPFLAG_NO_RETVAL, _T("m68k_return")));
|
|
|
|
exit_trap_trapaddr = here();
|
|
calltrap (deftrap2 (exit_trap_handler, TRAPFLAG_NO_RETVAL, _T("exit_trap")));
|
|
|
|
if(trap_mutex != 0)
|
|
uae_sem_destroy(&trap_mutex);
|
|
trap_mutex = 0;
|
|
uae_sem_init (&trap_mutex, 0, 1);
|
|
}
|
|
|
|
void trap_call_add_dreg(TrapContext *ctx, int reg, uae_u32 v)
|
|
{
|
|
ctx->calllib_reg_inuse[reg] = 1;
|
|
ctx->calllib_regs[reg] = v;
|
|
}
|
|
void trap_call_add_areg(TrapContext *ctx, int reg, uae_u32 v)
|
|
{
|
|
ctx->calllib_reg_inuse[reg + 8] = 1;
|
|
ctx->calllib_regs[reg + 8] = v;
|
|
}
|
|
uae_u32 trap_call_lib(TrapContext *ctx, uaecptr base, uae_s16 offset)
|
|
{
|
|
uae_u32 v;
|
|
uae_u32 storedregs[16];
|
|
bool storedregsused[16];
|
|
for (int i = 0; i < 16; i++) {
|
|
storedregsused[i] = false;
|
|
if (ctx->calllib_reg_inuse[i]) {
|
|
if ((i & 7) >= 2) {
|
|
storedregsused[i] = true;
|
|
storedregs[i] = regs.regs[i];
|
|
}
|
|
regs.regs[i] = ctx->calllib_regs[i];
|
|
}
|
|
ctx->calllib_reg_inuse[i] = 0;
|
|
}
|
|
v = CallLib(ctx, base, offset);
|
|
for (int i = 0; i < 16; i++) {
|
|
if (storedregsused[i]) {
|
|
regs.regs[i] = storedregs[i];
|
|
}
|
|
}
|
|
return v;
|
|
}
|
|
uae_u32 trap_call_func(TrapContext *ctx, uaecptr func)
|
|
{
|
|
uae_u32 v;
|
|
uae_u32 storedregs[16];
|
|
bool storedregsused[16];
|
|
for (int i = 0; i < 16; i++) {
|
|
storedregsused[i] = false;
|
|
if (ctx->calllib_reg_inuse[i]) {
|
|
if ((i & 7) >= 2) {
|
|
storedregsused[i] = true;
|
|
storedregs[i] = regs.regs[i];
|
|
}
|
|
regs.regs[i] = ctx->calllib_regs[i];
|
|
}
|
|
ctx->calllib_reg_inuse[i] = 0;
|
|
}
|
|
v = CallFunc(ctx, func);
|
|
for (int i = 0; i < 16; i++) {
|
|
if (storedregsused[i]) {
|
|
regs.regs[i] = storedregs[i];
|
|
}
|
|
}
|
|
return v;
|
|
}
|
|
|
|
|
|
bool trap_valid_address(TrapContext *ctx, uaecptr addr, uae_u32 size)
|
|
{
|
|
return valid_address(addr, size) != 0;
|
|
}
|
|
|
|
uae_u32 trap_get_dreg(TrapContext *ctx, int reg)
|
|
{
|
|
return m68k_dreg(regs, reg);
|
|
}
|
|
uae_u32 trap_get_areg(TrapContext *ctx, int reg)
|
|
{
|
|
return m68k_areg(regs, reg);
|
|
}
|
|
void trap_set_dreg(TrapContext *ctx, int reg, uae_u32 v)
|
|
{
|
|
m68k_dreg(regs, reg) = v;
|
|
}
|
|
void trap_set_areg(TrapContext *ctx, int reg, uae_u32 v)
|
|
{
|
|
m68k_areg(regs, reg) = v;
|
|
}
|
|
void trap_put_quad(TrapContext *ctx, uaecptr addr, uae_u64 v)
|
|
{
|
|
uae_u8 out[8];
|
|
put_long_host(out + 0, v >> 32);
|
|
put_long_host(out + 4, (uae_u32)(v >> 0));
|
|
trap_put_bytes(ctx, out, addr, 8);
|
|
}
|
|
void trap_put_long(TrapContext *ctx, uaecptr addr, uae_u32 v)
|
|
{
|
|
put_long(addr, v);
|
|
}
|
|
void trap_put_word(TrapContext *ctx, uaecptr addr, uae_u16 v)
|
|
{
|
|
put_word(addr, v);
|
|
}
|
|
void trap_put_byte(TrapContext *ctx, uaecptr addr, uae_u8 v)
|
|
{
|
|
put_byte(addr, v);
|
|
}
|
|
|
|
uae_u64 trap_get_quad(TrapContext *ctx, uaecptr addr)
|
|
{
|
|
uae_u8 in[8];
|
|
trap_get_bytes(ctx, in, addr, 8);
|
|
return ((uae_u64)get_long_host(in + 0) << 32) | get_long_host(in + 4);
|
|
}
|
|
uae_u32 trap_get_long(TrapContext *ctx, uaecptr addr)
|
|
{
|
|
return get_long(addr);
|
|
}
|
|
uae_u16 trap_get_word(TrapContext *ctx, uaecptr addr)
|
|
{
|
|
return get_word(addr);
|
|
}
|
|
uae_u8 trap_get_byte(TrapContext *ctx, uaecptr addr)
|
|
{
|
|
return get_byte(addr);
|
|
}
|
|
|
|
void trap_put_bytes(TrapContext *ctx, const void *haddrp, uaecptr addr, int cnt)
|
|
{
|
|
if (cnt <= 0)
|
|
return;
|
|
uae_u8 *haddr = (uae_u8*)haddrp;
|
|
if (valid_address(addr, cnt)) {
|
|
memcpy(get_real_address(addr), haddr, cnt);
|
|
} else {
|
|
for (int i = 0; i < cnt; i++) {
|
|
put_byte(addr, *haddr++);
|
|
addr++;
|
|
}
|
|
}
|
|
}
|
|
void trap_get_bytes(TrapContext *ctx, void *haddrp, uaecptr addr, int cnt)
|
|
{
|
|
if (cnt <= 0)
|
|
return;
|
|
uae_u8 *haddr = (uae_u8*)haddrp;
|
|
if (valid_address(addr, cnt)) {
|
|
memcpy(haddr, get_real_address(addr), cnt);
|
|
} else {
|
|
for (int i = 0; i < cnt; i++) {
|
|
*haddr++ = get_byte(addr);
|
|
addr++;
|
|
}
|
|
}
|
|
}
|
|
void trap_put_longs(TrapContext *ctx, uae_u32 *haddr, uaecptr addr, int cnt)
|
|
{
|
|
if (cnt <= 0)
|
|
return;
|
|
uae_u32 *p = (uae_u32*)haddr;
|
|
for (int i = 0; i < cnt; i++) {
|
|
put_long(addr, *p++);
|
|
addr += 4;
|
|
}
|
|
}
|
|
void trap_get_longs(TrapContext *ctx, uae_u32 *haddr, uaecptr addr, int cnt)
|
|
{
|
|
if (cnt <= 0)
|
|
return;
|
|
uae_u32 *p = (uae_u32*)haddr;
|
|
for (int i = 0; i < cnt; i++) {
|
|
*p++ = get_long(addr);
|
|
addr += 4;
|
|
}
|
|
}
|
|
void trap_put_words(TrapContext *ctx, uae_u16 *haddr, uaecptr addr, int cnt)
|
|
{
|
|
if (cnt <= 0)
|
|
return;
|
|
uae_u16 *p = (uae_u16*)haddr;
|
|
for (int i = 0; i < cnt; i++) {
|
|
put_word(addr, *p++);
|
|
addr += sizeof(uae_u16);
|
|
}
|
|
}
|
|
void trap_get_words(TrapContext *ctx, uae_u16 *haddr, uaecptr addr, int cnt)
|
|
{
|
|
if (cnt <= 0)
|
|
return;
|
|
uae_u16 *p = (uae_u16*)haddr;
|
|
for (int i = 0; i < cnt; i++) {
|
|
*p++ = get_word(addr);
|
|
addr += sizeof(uae_u16);
|
|
}
|
|
}
|
|
|
|
int trap_put_string(TrapContext *ctx, const void *haddrp, uaecptr addr, int maxlen)
|
|
{
|
|
int len = 0;
|
|
uae_u8 *haddr = (uae_u8*)haddrp;
|
|
for (;;) {
|
|
uae_u8 v = *haddr++;
|
|
put_byte(addr, v);
|
|
addr++;
|
|
if (!v)
|
|
break;
|
|
len++;
|
|
}
|
|
return len;
|
|
}
|
|
int trap_get_string(TrapContext *ctx, void *haddrp, uaecptr addr, int maxlen)
|
|
{
|
|
int len = 0;
|
|
uae_u8 *haddr = (uae_u8*)haddrp;
|
|
for (;;) {
|
|
uae_u8 v = get_byte(addr);
|
|
*haddr++ = v;
|
|
addr++;
|
|
if (!v)
|
|
break;
|
|
}
|
|
len++;
|
|
return len;
|
|
}
|
|
uae_char *trap_get_alloc_string(TrapContext *ctx, uaecptr addr, int maxlen)
|
|
{
|
|
uae_char *buf = xmalloc(uae_char, maxlen);
|
|
trap_get_string(ctx, buf, addr, maxlen);
|
|
return buf;
|
|
}
|
|
|
|
int trap_get_bstr(TrapContext *ctx, uae_u8 *haddr, uaecptr addr, int maxlen)
|
|
{
|
|
int len = 0;
|
|
uae_u8 cnt = get_byte(addr);
|
|
while (cnt-- != 0 && maxlen-- > 0) {
|
|
addr++;
|
|
*haddr++ = get_byte(addr);
|
|
}
|
|
*haddr = 0;
|
|
return len;
|
|
}
|
|
|
|
void trap_set_longs(TrapContext *ctx, uaecptr addr, uae_u32 v, int cnt)
|
|
{
|
|
if (cnt <= 0)
|
|
return;
|
|
for (int i = 0; i < cnt; i++) {
|
|
put_long(addr, v);
|
|
addr += 4;
|
|
}
|
|
}
|
|
void trap_set_words(TrapContext *ctx, uaecptr addr, uae_u16 v, int cnt)
|
|
{
|
|
if (cnt <= 0)
|
|
return;
|
|
for (int i = 0; i < cnt; i++) {
|
|
put_word(addr, v);
|
|
addr += 2;
|
|
}
|
|
}
|
|
void trap_set_bytes(TrapContext *ctx, uaecptr addr, uae_u8 v, int cnt)
|
|
{
|
|
if (cnt <= 0)
|
|
return;
|
|
for (int i = 0; i < cnt; i++) {
|
|
put_byte(addr, v);
|
|
addr += 1;
|
|
}
|
|
}
|
|
|
|
void trap_multi(TrapContext *ctx, struct trapmd *data, int items)
|
|
{
|
|
uae_u32 v = 0;
|
|
for (int i = 0; i < items; i++) {
|
|
struct trapmd *md = &data[i];
|
|
switch (md->cmd)
|
|
{
|
|
case TRAPCMD_PUT_LONG:
|
|
trap_put_long(ctx, md->params[0], md->params[1]);
|
|
break;
|
|
case TRAPCMD_PUT_WORD:
|
|
trap_put_word(ctx, md->params[0], md->params[1]);
|
|
break;
|
|
case TRAPCMD_PUT_BYTE:
|
|
trap_put_byte(ctx, md->params[0], md->params[1]);
|
|
break;
|
|
case TRAPCMD_GET_LONG:
|
|
v = md->params[0] = trap_get_long(ctx, md->params[0]);
|
|
break;
|
|
case TRAPCMD_GET_WORD:
|
|
v = md->params[0] = trap_get_word(ctx, md->params[0]);
|
|
break;
|
|
case TRAPCMD_GET_BYTE:
|
|
v = md->params[0] = trap_get_byte(ctx, md->params[0]);
|
|
break;
|
|
case TRAPCMD_PUT_BYTES:
|
|
trap_put_bytes(ctx, md->haddr, md->params[0], md->params[1]);
|
|
break;
|
|
case TRAPCMD_GET_BYTES:
|
|
trap_get_bytes(ctx, md->haddr, md->params[0], md->params[1]);
|
|
break;
|
|
case TRAPCMD_PUT_WORDS:
|
|
trap_put_words(ctx, (uae_u16*)md->haddr, md->params[0], md->params[1]);
|
|
break;
|
|
case TRAPCMD_GET_WORDS:
|
|
trap_get_words(ctx, (uae_u16*)md->haddr, md->params[0], md->params[1]);
|
|
break;
|
|
case TRAPCMD_PUT_LONGS:
|
|
trap_put_longs(ctx, (uae_u32*)md->haddr, md->params[0], md->params[1]);
|
|
break;
|
|
case TRAPCMD_GET_LONGS:
|
|
trap_get_longs(ctx, (uae_u32*)md->haddr, md->params[0], md->params[1]);
|
|
break;
|
|
case TRAPCMD_PUT_STRING:
|
|
trap_put_string(ctx, md->haddr, md->params[0], md->params[1]);
|
|
break;
|
|
case TRAPCMD_GET_STRING:
|
|
trap_get_string(ctx, md->haddr, md->params[0], md->params[1]);
|
|
break;
|
|
case TRAPCMD_SET_LONGS:
|
|
trap_set_longs(ctx, md->params[0], md->params[1], md->params[2]);
|
|
break;
|
|
case TRAPCMD_SET_WORDS:
|
|
trap_set_words(ctx, md->params[0], md->params[1], md->params[2]);
|
|
break;
|
|
case TRAPCMD_SET_BYTES:
|
|
trap_set_bytes(ctx, md->params[0], md->params[1], md->params[2]);
|
|
break;
|
|
case TRAPCMD_NOP:
|
|
break;
|
|
}
|
|
if (md->trapmd_index) {
|
|
data[md->trapmd_index].params[md->parm_num] = v;
|
|
}
|
|
}
|
|
}
|
|
|
|
void trap_memcpyha_safe(TrapContext *ctx, uaecptr dst, const uae_u8 *src, int size)
|
|
{
|
|
if (size <= 0)
|
|
return;
|
|
memcpyha_safe(dst, src, size);
|
|
}
|
|
void trap_memcpyah_safe(TrapContext *ctx, uae_u8 *dst, uaecptr src, int size)
|
|
{
|
|
if (size <= 0)
|
|
return;
|
|
memcpyah_safe(dst, src, size);
|
|
}
|