Add sandbox frame callback, make memory alignment test

This commit is contained in:
meepingsnesroms
2019-04-11 09:51:26 -07:00
parent 432233d8f1
commit f2233197e4
9 changed files with 162 additions and 71 deletions

View File

@@ -19,14 +19,14 @@ static ArmCpu armv5Cpu;
static Boolean armv5MemoryAccess(ArmCpu* cpu, void* buf, UInt32 vaddr, UInt8 size, Boolean write, Boolean privileged, UInt8* fsr){
//just gonna have ARM and m68k share the same bus for now, ARM may want fonts/bitmaps from ROM
//just gonna have ARM and 68k share the same bus for now, ARM may want fonts/bitmaps from ROM
//Fake ARM 32 bit alignment convention
//0x00000000<->0x7FFFFFFF Normal memory access
//0x80000000<->0xFFFFFFFF Mirrored range, realAddress = address - 0x80000000 + 0x00000002
//0x0XXXXXXX<->0x1XXXXXXX Normal memory access
//0x20000000<->0x3XXXXXXX Mirrored range, realAddress = address - 0x20000000 + 0x00000002
//this will still fail if ARM allocates a 16 bit aligned buffer and passes it to the m68k,
//but it allows 16 bit aligned data to be executed from the m68k stack and works as a
//but it allows 16 bit aligned data to be executed from the 68k stack and works as a
//temporary measure until I get MemChunkNew patched correctly
//while executing wont support non 32 bit alignment regular accesses can for now
@@ -40,8 +40,8 @@ static Boolean armv5MemoryAccess(ArmCpu* cpu, void* buf, UInt32 vaddr, UInt8 siz
#endif
*/
if(vaddr & 0x80000000)
vaddr ^= 0x80000002;
if(vaddr & 0x20000000)
vaddr ^= 0x20000002;
if(write){
switch(size){
@@ -97,7 +97,7 @@ static void armv5SetFaultAddr(struct ArmCpu* cpu, UInt32 adr, UInt8 faultStatus)
}
void armv5Reset(void){
cpuInit(&armv5Cpu, 0x00000000/*PC, set by m68k while emulating*/, armv5MemoryAccess, armv5EmulErr, armv5Hypercall, armv5SetFaultAddr);
cpuInit(&armv5Cpu, 0x00000000/*PC, set by 68k while emulating*/, armv5MemoryAccess, armv5EmulErr, armv5Hypercall, armv5SetFaultAddr);
armv5ServiceRequest = false;
}

View File

@@ -3,7 +3,7 @@
#if defined(EMU_DEBUG) && defined(EMU_SANDBOX)
//this wrappers m68k code and allows calling it for tests, this should be useful for determining if hardware accesses are correct
//this wrappers 68k code and allows calling it for tests, this should be useful for determining if hardware accesses are correct
//Note: when running a test the emulator runs at native speed and no clocks are emulated
#include <stdlib.h>
#include <string.h>
@@ -25,6 +25,8 @@
#define SANDBOX_MAX_UNLOGGED_JUMP_SIZE 0xFFFFFFFF
#define SANDBOX_MAX_WATCH_REGIONS 1000
#define SANDBOX_SECONDS_TO_FRAMES(x) ((x) * EMU_FPS)
typedef struct{
void* hostPointer;
@@ -51,7 +53,8 @@ static bool sandboxActive;//used to "log out" of the emulator once
static bool sandboxControlHandoff;//used for functions that depend on timing, hands full control to the m68k
static uint8_t sandboxCurrentCpuArch;
static local_cpu_state_t sandboxOldFunctionCpuState;
static mem_region_t sandboxWatchRegions[SANDBOX_MAX_WATCH_REGIONS];//code locations in m68k address space to be sandboxed
static uint64_t sandboxFramesRan;
static mem_region_t sandboxWatchRegions[SANDBOX_MAX_WATCH_REGIONS];//code locations in 68k address space to be sandboxed
static uint16_t sandboxWatchRegionsActive;//number of used sandboxWatchRegions entrys
@@ -69,6 +72,17 @@ static uint32_t getCurrentCpuPreviousPc(void){
return sandboxCurrentCpuArch == SANDBOX_CPU_ARCH_M68K ? m68k_get_reg(NULL, M68K_REG_PPC) : armv5GetRegister(15) & 0xFFFFFFFE;
}
uint32_t getRandomRange(uint32_t start, uint32_t end){
static bool seeded = false;
if(!seeded){
srand(time(NULL));
seeded = true;
}
return (uint32_t)rand() % (end + 1 - start) + start;
}
static char* takeStackDump(uint32_t bytes){
char* textBytes = malloc(bytes * 2);
uint32_t textBytesOffset = 0;
@@ -166,6 +180,8 @@ bool validExecutionAddress(uint32_t address){
return true;
if(address >= chips[CHIP_DX_RAM].start && address < chips[CHIP_DX_RAM].start + chips[CHIP_DX_RAM].lineSize)
return true;
if(sandboxActive && address >= 0xFFFFFE00)//used to run custom code when in sandbox mode
return true;
return false;
}
@@ -401,6 +417,72 @@ static bool installResourceToDevice(buffer_t resourceBuffer){
return true;
}
static void checkMemoryAlignment(uint16_t heap){
uint32_t memPtrs[100];
uint8_t index;
uint16_t error;
memset(memPtrs, 0x00, sizeof(memPtrs));
//get randomly sized memory regions
for(index = 0; index < 100; index++){
memPtrs[index] = sandboxCallGuestFunction(false, 0x00000000, MemChunkNew, "p(wlw)", heap, getRandomRange(1, 100), 0x0200/*memNewChunkFlagNonMovable*/);
if(!memPtrs[index]){
debugLog("Memory test: Failed to allocate memory\n");
goto failed;
}
}
//check alignment
for(index = 0; index < 100; index++){
if(memPtrs[index] & 0x00000003){
debugLog("Memory test: Memory allocations are not 32 bit aligned:0x%08X\n", memPtrs[index]);
goto failed;
}
}
//resize memory
for(index = 0; index < 100; index++){
error = sandboxCallGuestFunction(false, 0x00000000, MemPtrResize, "w(pl)", memPtrs[index], getRandomRange(1, 100));
if(error != 0x0000/*errNone*/){
debugLog("Memory test: Failed to resize memory:0x%04X\n", error);
goto failed;
}
}
//check alignment again
for(index = 0; index < 100; index++){
if(memPtrs[index] & 0x00000003){
debugLog("Memory test: Memory misaligned by resize:0x%08X\n", memPtrs[index]);
goto failed;
}
}
//shuffle memory
error = sandboxCallGuestFunction(false, 0x00000000, MemHeapScramble, "w(w)", heap);
if(error != 0x0000/*errNone*/){
debugLog("Memory test: Unable to scramble heap:0x%04X\n", error);
goto failed;
}
//check alignment again
for(index = 0; index < 100; index++){
if(memPtrs[index] & 0x00000003){
debugLog("Memory test: Memory misaligned by defragment:0x%08X\n", memPtrs[index]);
goto failed;
}
}
debugLog("Memory test: Memory aligned correctly\n");
failed:
//free pointers
for(index = 0; index < 100; index++)
if(memPtrs[index])
sandboxCallGuestFunction(false, 0x00000000, MemChunkFree, "w(p)", memPtrs[index]);
}
static uint32_t sandboxGetStackFrameSize(const char* prototype){
const char* params = prototype + 2;
@@ -607,28 +689,17 @@ void sandboxInit(void){
void sandboxReset(void){
sandboxActive = false;
sandboxControlHandoff = false;
sandboxFramesRan = 0;
memset(sandboxWatchRegions, 0x00, sizeof(sandboxWatchRegions));
sandboxWatchRegionsActive = 0;
//patch OS here if needed
//debug patches
//sandboxCommand(SANDBOX_PATCH_OS, NULL);
//add memory region monitors
/*
if(pc >= 0x100A07DC && pc <= 0x100ADB3C){
//the SD slot driver
return true;
}
*/
sandboxCommand(SANDBOX_CMD_PATCH_OS, NULL);
//log all register accesses
//sandboxCommand(SANDBOX_CMD_REGISTER_WATCH_ENABLE, NULL);
//patch OS
sandboxCommand(SANDBOX_CMD_PATCH_OS, NULL);
//monitor for strange jumps
sandboxSetWatchRegion(0x00000000, 0xFFFFFFFE, SANDBOX_WATCH_CODE);
}
@@ -639,6 +710,7 @@ uint32_t sandboxStateSize(void){
size += sizeof(uint8_t) * 3;//sandboxActive / sandboxControlHandoff / sandboxCurrentCpuArch
size += sizeof(uint32_t) * 4;//sandboxOldFunctionCpuState.(sp/pc/a0/d0)
size += sizeof(uint16_t);//sandboxOldFunctionCpuState.sr
size += sizeof(uint64_t);//sandboxFramesRan
size += sizeof(uint32_t) * 2 * SANDBOX_MAX_WATCH_REGIONS;//sandboxWatchRegions.(address/size)
size += sizeof(uint8_t) * SANDBOX_MAX_WATCH_REGIONS;//sandboxWatchRegions.type
size += sizeof(uint16_t);//sandboxWatchRegionsActive
@@ -666,6 +738,8 @@ void sandboxSaveState(uint8_t* data){
offset += sizeof(uint32_t);
writeStateValue32(data + offset, sandboxOldFunctionCpuState.d0);
offset += sizeof(uint32_t);
writeStateValue64(data + offset, sandboxFramesRan);
offset += sizeof(uint64_t);
for(index = 0; index < SANDBOX_MAX_WATCH_REGIONS; index++){
writeStateValue32(data + offset, sandboxWatchRegions[index].address);
offset += sizeof(uint32_t);
@@ -698,6 +772,8 @@ void sandboxLoadState(uint8_t* data){
offset += sizeof(uint32_t);
sandboxOldFunctionCpuState.d0 = readStateValue32(data + offset);
offset += sizeof(uint32_t);
sandboxFramesRan = readStateValue64(data + offset);
offset += sizeof(uint64_t);
for(index = 0; index < SANDBOX_MAX_WATCH_REGIONS; index++){
sandboxWatchRegions[index].address = readStateValue32(data + offset);
offset += sizeof(uint32_t);
@@ -721,20 +797,6 @@ uint32_t sandboxCommand(uint32_t command, void* data){
switch(command){
case SANDBOX_CMD_PATCH_OS:{
//this will not be in the v1.0 release
//remove parts of the OS that cause lockups, yeah its bad
//remove ErrDisplayFileLineMsg from HwrIRQ2Handler, device locks on USB polling without this
//this is fixed, leaving it here for reference
//patchOsRom(0x83652, "4E714E71");//nop; nop
//make SysSetTrapAddress/SysGetTrapAddress support traps > ScrDefaultPaletteState 0xA459
//up to final OS 5.3 trap DmSyncDatabase 0xA476
//SysSetTrapAddress_1001AE36:
//SysGetTrapAddress_1001AE7C:
//patchOsRom(0x1AE42, "0C410477");//cmpi.w 0x477, d1
//patchOsRom(0x1AE8A, "0C42A477");//cmpi.w 0xA477, d2
//double dynamic heap size, verified working
//HwrCalcDynamicRAMSize_10005CC6:
//HwrCalcDynamicRAMSize_10083B0A:
@@ -881,6 +943,12 @@ uint32_t sandboxCommand(uint32_t command, void* data){
}
break;
case SANDBOX_CMD_TEST_MEMORY_ALIGNMENT:{
checkMemoryAlignment(0);//RAM heap
checkMemoryAlignment(1);//storage heap
}
break;
default:
break;
}
@@ -890,6 +958,15 @@ uint32_t sandboxCommand(uint32_t command, void* data){
return result;
}
void sandboxOnFrameRun(void){
//run at the end of every frame
sandboxFramesRan++;
if(sandboxFramesRan == SANDBOX_SECONDS_TO_FRAMES(10)){
sandboxCommand(SANDBOX_CMD_TEST_MEMORY_ALIGNMENT, NULL);
}
}
void sandboxOnOpcodeRun(void){
if(sandboxRunning()){
#if defined(EMU_SANDBOX_LOG_JUMPS)
@@ -1135,6 +1212,7 @@ uint32_t sandboxStateSize(void){return 0;}
void sandboxSaveState(uint8_t* data){}
void sandboxLoadState(uint8_t* data){}
uint32_t sandboxCommand(uint32_t command, void* data){return 0;}
void sandboxOnFrameRun(void){}
void sandboxOnOpcodeRun(void){}
void sandboxOnMemoryAccess(uint32_t address, uint8_t size, bool write, uint32_t value){}
bool sandboxRunning(void){return false;}

View File

@@ -7,7 +7,8 @@
enum{
SANDBOX_CMD_PATCH_OS = 0,
SANDBOX_CMD_DEBUG_INSTALL_APP,
SANDBOX_CMD_REGISTER_WATCH_ENABLE
SANDBOX_CMD_REGISTER_WATCH_ENABLE,
SANDBOX_CMD_TEST_MEMORY_ALIGNMENT
};
enum{
@@ -29,10 +30,11 @@ uint32_t sandboxStateSize(void);
void sandboxSaveState(uint8_t* data);
void sandboxLoadState(uint8_t* data);
uint32_t sandboxCommand(uint32_t command, void* data);
void sandboxOnFrameRun(void);
void sandboxOnOpcodeRun(void);
void sandboxOnMemoryAccess(uint32_t address, uint8_t size, bool write, uint32_t value);
bool sandboxRunning(void);
void sandboxReturn(void);//should only be called called by m68k code
void sandboxReturn(void);//should only be called called by 68k code
uint16_t sandboxSetWatchRegion(uint32_t address, uint32_t size, uint8_t type);
void sandboxClearWatchRegion(uint16_t index);
void sandboxSetCpuArch(uint8_t arch);

View File

@@ -733,4 +733,8 @@ void emulatorRunFrame(void){
memcpy(palmFramebuffer, sed1376Framebuffer, 160 * 160 * sizeof(uint16_t));
memcpy(palmFramebuffer + 160 * 160, silkscreen160x60, 160 * 60 * sizeof(uint16_t));
}
#if defined(EMU_SANDBOX)
sandboxOnFrameRun();
#endif
}

View File

@@ -42,7 +42,7 @@ void flx68000PcLongJump(uint32_t newPc){
memBase = dataBufferHost - dataBufferGuest - windowSize * ((newPc - dataBufferGuest) / windowSize);
}
//everything must be 16 bit aligned(accept 8 bit accesses) due to m68k unaligned access rules,
//everything must be 16 bit aligned(accept 8 bit accesses) due to 68k unaligned access rules,
//32 bit reads are 2 16 bit reads because on some platforms 32 bit reads that arnt on 32 bit boundrys will crash the program
#if defined(EMU_BIG_ENDIAN)
uint16_t m68k_read_immediate_16(uint32_t address){
@@ -279,7 +279,7 @@ bool flx68000IsSupervisor(void){
void flx68000BusError(uint32_t address, bool isWrite){
#if !defined(EMU_NO_SAFETY)
if(!(palmEmuFeatures.info & FEATURE_DURABLE)){
//never call outsize of a m68k opcode, behavior is undefined due to longjmp
//never call outsize of a 68k opcode, behavior is undefined due to longjmp
m68ki_trigger_bus_error(address, isWrite ? MODE_WRITE : MODE_READ, FLAG_S | m68ki_get_address_space());
}
#endif

View File

@@ -53,7 +53,7 @@ These registers will do nothing if their corresponding feature bit is not set on
#define CMD_DEBUG_WATCH 0x0000FFF9/*EMU_VALUE = 0/clear watch reference or 1/make code watch reference or 2/make data watch reference, EMU_SRC = address of area(or watch area reference number), EMU_SIZE = size of area(unused if clear operation), EMU_VALUE is set to the watch areas reference number after a set operation*/
#define CMD_SOUND 0x0000FFFA/*needed for OS 5 advanced sound*/
#define CMD_DEBUG_EXEC_END 0x0000FFFB/*terminates execution, used when a function is called from outside the emulator*/
#define CMD_ARM_SERVICE 0x0000FFFC/*EMU_VALUE is set to 1 if ARM wants service from m68k routines*/
#define CMD_ARM_SERVICE 0x0000FFFC/*EMU_VALUE is set to 1 if ARM wants service from 68k routines*/
#define CMD_ARM_SET_REG 0x0000FFFD/*EMU_DST = register number, EMU_VALUE = new value*/
#define CMD_ARM_GET_REG 0x0000FFFE/*EMU_SRC = register number, EMU_VALUE is set to register value*/
#define CMD_ARM_RUN 0x0000FFFF/*EMU_VALUE = cycles, EMU_VALUE is set to cycles not used on return, 0 if all are used*/

View File

@@ -17,9 +17,16 @@ void armv5SetStack(uint8_t* location, uint32_t size){
}
uint32_t* armv5ValidatePointer(uint32_t* address){
/*patches 16 bit alignment to 32 bit alignment using a special memory range*/
/*patches 16 bit alignment to 32 bit alignment using a special address bit*/
if((uint32_t)address & 0x00000002)
return (uint32_t*)((uint32_t)address ^ 0x80000002);
return (uint32_t*)((uint32_t)address ^ 0x20000002);
return address;
}
uint32_t* armv5InvalidatePointer(uint32_t* address){
/*puts address bits back to normal*/
if((uint32_t)address & 0x20000000)
return (uint32_t*)((uint32_t)address ^ 0x20000002);
return address;
}

View File

@@ -6,6 +6,7 @@
void armv5SetStack(uint8_t* location, uint32_t size);
uint32_t* armv5ValidatePointer(uint32_t* address);
uint32_t* armv5InvalidatePointer(uint32_t* address);
void armv5StackPush(uint32_t value);
uint32_t armv5StackPop(void);
void armv5SetRegister(uint8_t reg, uint32_t value);

View File

@@ -12,20 +12,35 @@
/*cant use normal global variables in this file!!!*/
/*functions in this file are called directly by the Palm OS trap dispatch, this means they can be called when the app isnt loaded and the globals are just a random data buffer*/
#if 0
static const SECTION(".text") ALIGN(2) uint32_t armExitFunc[] = {0x0000A0E3, 0xBBBBBBF7};/*ARM asm blob*/
static const SECTION(".text") ALIGN(2) uint32_t armCall68kFunc[] = {0x0100A0E3, 0xBBBBBBF7, 0x0EF0A0E1};/*ARM asm blob*/
static const SECTION(".text") ALIGN(2) uint8_t m68kCallWithBlobFunc[] = {
0x4E, 0x56, 0x00, 0x00, 0x48, 0xE7, 0x60, 0x70, 0x26, 0x6E, 0x00, 0x08,
0x22, 0x6E, 0x00, 0x0C, 0x22, 0x2E, 0x00, 0x10, 0x34, 0x2E, 0x00, 0x14,
0x24, 0x4F, 0x95, 0xC1, 0xBF, 0xCA, 0x67, 0x00, 0x00, 0x0A, 0x34, 0x91,
0x54, 0x89, 0x54, 0x8A, 0x60, 0xF2, 0x9F, 0xC1, 0x4E, 0x93, 0xDF, 0xC1,
0x4A, 0x42, 0x67, 0x00, 0x00, 0x04, 0x20, 0x08, 0x4C, 0xDF, 0x0E, 0x06,
0x4E, 0x5E, 0x4E, 0x75
};/*68k asm blob*/
#endif
UInt32 emuPceNativeCall(NativeFuncType* nativeFuncP, void* userDataP){
/*these arnt set as "static" because the globals dont work here*/
#if 1
const ALIGN(2) uint32_t armExitFunc[] = {0x0000A0E3, 0xBBBBBBF7};/*ARM asm blob*/
const ALIGN(2) uint32_t armCall68kFunc[] = {0x0100A0E3, 0xBBBBBBF7, 0x0EF0A0E1};/*ARM asm blob*/
const ALIGN(2) uint8_t m68kCallWithBlobFunc[] = {
0x4e, 0x56, 0x00, 0x00, 0x48, 0xe7, 0x60, 0x70, 0x26, 0x6e, 0x00, 0x08,
0x22, 0x6e, 0x00, 0x0c, 0x22, 0x2e, 0x00, 0x10, 0x34, 0x2e, 0x00, 0x14,
0x24, 0x4f, 0x95, 0xc1, 0xbf, 0xca, 0x67, 0x00, 0x00, 0x0a, 0x34, 0x91,
0x54, 0x89, 0x54, 0x8a, 0x60, 0xf2, 0x9f, 0xc1, 0x4e, 0x93, 0xdf, 0xc1,
0x4a, 0x42, 0x67, 0x00, 0x00, 0x04, 0x20, 0x08, 0x4c, 0xdf, 0x0e, 0x06,
0x4e, 0x5e, 0x4e, 0x75
0x4E, 0x56, 0x00, 0x00, 0x48, 0xE7, 0x60, 0x70, 0x26, 0x6E, 0x00, 0x08,
0x22, 0x6E, 0x00, 0x0C, 0x22, 0x2E, 0x00, 0x10, 0x34, 0x2E, 0x00, 0x14,
0x24, 0x4F, 0x95, 0xC1, 0xBF, 0xCA, 0x67, 0x00, 0x00, 0x0A, 0x34, 0x91,
0x54, 0x89, 0x54, 0x8A, 0x60, 0xF2, 0x9F, 0xC1, 0x4E, 0x93, 0xDF, 0xC1,
0x4A, 0x42, 0x67, 0x00, 0x00, 0x04, 0x20, 0x08, 0x4C, 0xDF, 0x0E, 0x06,
0x4E, 0x5E, 0x4E, 0x75
};/*m68k asm blob*/
};/*68k asm blob*/
#endif
uint32_t oldArmRegisters[5];
uint32_t returnValue;
@@ -40,11 +55,11 @@ UInt32 emuPceNativeCall(NativeFuncType* nativeFuncP, void* userDataP){
/*when R0 = 1, R1 = function to execute, R2 = stack blob, R3 = stack blob size and want A0*/
/*Fake ARM 32 bit alignment convention*/
/*0x00000000<->0x7FFFFFFF Normal memory access*/
/*0x80000000<->0xFFFFFFFF Mirrored range, realAddress = address - 0x80000000 + 0x00000002*/
/*0x0XXXXXXX<->0x1XXXXXXX Normal memory access*/
/*0x20000000<->0x3XXXXXXX Mirrored range, realAddress = address - 0x20000000 + 0x00000002*/
/*this will still fail if ARM allocates a 16 bit aligned buffer and passes it to the m68k,*/
/*but it allows 16 bit aligned data to be executed from the m68k stack and works as a*/
/*this will still fail if ARM allocates a 16 bit aligned buffer and passes it to the 68k,*/
/*but it allows 16 bit aligned data to be executed from the 68k stack and works as a*/
/*temporary measure until I get MemChunkNew patched correctly*/
debugLog("Called ARM function:0x%08lX\n", (uint32_t)nativeFuncP);
@@ -70,28 +85,12 @@ UInt32 emuPceNativeCall(NativeFuncType* nativeFuncP, void* userDataP){
/*ARM tried to call a 68k function or has finished executing*/
if(armv5GetRegister(0)){
/*call function*/
uint32_t emulStateP = armv5GetRegister(0);
uint32_t function = armv5GetRegister(1);
uint32_t stackBlob = armv5GetRegister(2);
uint32_t stackBlobSizeAndWantA0 = armv5GetRegister(3);
uint32_t (*m68kCallWithBlobFuncPtr)(uint32_t functionAddress, uint32_t stackBlob, uint32_t stackBlobSize, uint16_t returnA0) = (uint32_t (*)(uint32_t, uint32_t, uint32_t, uint16_t))m68kCallWithBlobFunc;
/*debug checks, I think these where preventing the callback to 68k code MemPtrNew???*/
/*
if(true){
uint32_t index;
uint32_t end = stackBlobSizeAndWantA0 & ~kPceNativeWantA0;
debugLog("ARM calling 68k function:0x%08lX, stackBlob:0x%08lX, stackBlobSize:0x%08lX, wantA0:%d\n", function, stackBlob, stackBlobSizeAndWantA0 & ~kPceNativeWantA0, !!(stackBlobSizeAndWantA0 & kPceNativeWantA0));
for(index = 0; index < end; index++)
debugLog("Stack byte:0x%02X\n", ((uint8_t*)stackBlob)[index]);
}
*/
/*debugLog("Called 68k function:0x%08lX\n", function);*/
debugLog("ARM calling 68k: emulStateP:0x%08lX, function:0x%08lX, stackBlob:0x%08lX, stackBlobSize:0x%08lX, wantA0:%d\n", emulStateP, function, stackBlob, stackBlobSizeAndWantA0 & ~kPceNativeWantA0, !!(stackBlobSizeAndWantA0 & kPceNativeWantA0));
/*
debugLog("ARM calling 68k function:0x%08lX, stackBlob:0x%08lX, stackBlobSize:0x%08lX, wantA0:%d\n", function, stackBlob, stackBlobSizeAndWantA0 & ~kPceNativeWantA0, !!(stackBlobSizeAndWantA0 & kPceNativeWantA0));
*/
debugLog("ARM calling 68k: function:0x%08lX, stackBlob:0x%08lX, stackBlobSize:0x%08lX, wantA0:%d\n", function, stackBlob, stackBlobSizeAndWantA0 & ~kPceNativeWantA0, !!(stackBlobSizeAndWantA0 & kPceNativeWantA0));
/*API call, convert to address first*/
if(function <= kPceNativeTrapNoMask)