aboutsummaryrefslogtreecommitdiff
path: root/SrcShared/EmPalmOS.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SrcShared/EmPalmOS.cpp')
-rw-r--r--SrcShared/EmPalmOS.cpp1752
1 files changed, 1752 insertions, 0 deletions
diff --git a/SrcShared/EmPalmOS.cpp b/SrcShared/EmPalmOS.cpp
new file mode 100644
index 0000000..dcb260f
--- /dev/null
+++ b/SrcShared/EmPalmOS.cpp
@@ -0,0 +1,1752 @@
+/* -*- mode: C++; tab-width: 4 -*- */
+/* ===================================================================== *\
+ Copyright (c) 2000-2001 Palm, Inc. or its subsidiaries.
+ All rights reserved.
+
+ This file is part of the Palm OS Emulator.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+\* ===================================================================== */
+
+#include "EmCommon.h"
+#include "EmPalmOS.h"
+
+#include "EmBankDRAM.h" // EmBankDRAM::SetLong
+#include "EmBankMapped.h" // EmBankMapped::SetLong
+#include "EmBankROM.h" // EmBankROM::SetLong
+#include "EmBankSRAM.h" // EmBankSRAM::SetLong
+
+
+#include "DebugMgr.h" // Debug::HandleSystemCall
+#include "EmCPU68K.h" // gCPU68K, gStackHigh, etc.
+#include "EmErrCodes.h" // kError_UnimplementedTrap, kError_InvalidLibraryRefNum
+#include "EmMemory.h" // CEnableFullAccess
+#include "EmPalmHeap.h" // EmPalmHeap, GetHeapByPtr
+#include "EmPalmFunction.h" // ProscribedFunction
+#include "EmPalmStructs.h" // EmAliasCardHeaderType
+#include "EmPatchMgr.h" // EmPatchMgr
+#include "EmPatchState.h" // EmPatchState
+#include "EmSession.h" // gSession->Reset
+#include "ErrorHandling.h" // Errors::ReportInvalidPC
+#include "Logging.h" // LogSystemCalls
+#include "MetaMemory.h" // MetaMemory::InRAMOSComponent
+#include "Miscellaneous.h" // GetSystemCallContext
+#include "Profiling.h" // gProfilingEnabled
+#include "ROMStubs.h" // IntlSetStrictChecks
+#include "UAE.h" // CHECK_STACK_POINTER_DECREMENT
+
+#include "EmEventPlayback.h" // EmEventPlayback::Initialize ();
+#include "EmLowMem.h" // EmLowMem::Initialize ();
+#include "EmPalmFunction.h" // EmPalmFunctionInit ();
+#include "EmPalmHeap.h" // EmPalmHeap::Initialize ();
+#include "EmPatchMgr.h" // EmPatchMgr::Initialize ();
+#include "Hordes.h" // Hordes::Initialize ();
+#include "Platform_NetLib.h" // Platform_NetLib::Initialize();
+
+#define LOG_FUNCTION_CALLS 0
+
+static StackList gStackList;
+static StackRange gBootStack;
+static StackRange gKernelStack;
+static StackRange gInterruptStack;
+
+static emuptr gBigROMEntry;
+
+static const uint32 CJ_TAGAMX = 0x414D5800;
+static const uint32 CJ_TAGFENCE = 0x55555555;
+
+// Subtract 34 bytes from the size of the stack. That's because
+// if an interrupt occurs, 34 bytes are pushed onto the stack before
+// the OS switches over to the interrupt stack.
+//
+// Exception 6 bytes // Old SR and old PC
+// LINK 4 bytes // Old A6
+// MOVEM.L 20 bytes // Old D0, D1, D2, A0, A1
+// MOVE A0, -(SP) 4 bytes // Old A0 (again)
+// ---------------------------------------------------------
+// Total 34 bytes
+
+static const int kInterruptOverhead = 34;
+static emuptr gStackLowWaterMark = 0;
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::Initialize
+ *
+ * DESCRIPTION: Standard initialization function. Responsible for
+ * initializing this sub-system when a new session is
+ * created. Will be followed by at least one call to
+ * Reset or Load.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::Initialize (void)
+{
+ EmAssert (gCPU68K);
+
+ gCPU68K->InstallHookException (kException_SysCall, HandleTrap15);
+
+ gCPU68K->InstallHookJSR (HandleJSR);
+ gCPU68K->InstallHookJSR_Ind (HandleJSR_Ind);
+ gCPU68K->InstallHookLINK (HandleLINK);
+ gCPU68K->InstallHookRTE (HandleRTE);
+ gCPU68K->InstallHookRTS (HandleRTS);
+ gCPU68K->InstallHookNewPC (HandleNewPC);
+ gCPU68K->InstallHookNewSP (HandleNewSP);
+
+ gBigROMEntry = EmMemNULL;
+
+ // Add a notification for IntlStrictChecks
+
+ gPrefs->AddNotification (&EmPalmOS::PrefsChanged, kPrefKeyReportStrictIntlChecks);
+ gPrefs->AddNotification (&EmPalmOS::PrefsChanged, kPrefKeyReportOverlayErrors);
+
+ Hordes::Initialize ();
+ EmEventPlayback::Initialize ();
+ EmPatchMgr::Initialize ();
+ Platform_NetLib::Initialize ();
+ EmPalmHeap::Initialize ();
+ EmLowMem::Initialize ();
+ EmPalmFunctionInit ();
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::Reset
+ *
+ * DESCRIPTION: Standard reset function. Sets the sub-system to a
+ * default state. This occurs not only on a Reset (as
+ * from the menu item), but also when the sub-system
+ * is first initialized (Reset is called after Initialize)
+ * as well as when the system is re-loaded from an
+ * insufficient session file.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::Reset (void)
+{
+ gStackList.clear ();
+
+ gBootStack = StackRange ();
+ gKernelStack = StackRange ();
+ gInterruptStack = StackRange ();
+
+ gKernelStackOverflowed = 0;
+
+ Hordes::Reset ();
+ EmEventPlayback::Reset ();
+ EmPatchMgr::Reset ();
+ Platform_NetLib::Reset ();
+ EmPalmHeap::Reset ();
+ EmLowMem::Reset ();
+
+ // If the appropriate modifier key is down, install a temporary breakpoint
+ // at the start of the Big ROM.
+
+ if (Platform::StopOnResetKeyDown ())
+ {
+ emuptr romStart = EmBankROM::GetMemoryStart ();
+ emuptr headerVersion = EmMemGet32 (romStart + offsetof (CardHeaderType, hdrVersion));
+ long bigROMOffset = 0x03000;
+
+ if (headerVersion > 1)
+ {
+ bigROMOffset = EmMemGet32 (romStart + offsetof (CardHeaderType, bigROMOffset));
+ bigROMOffset &= 0x000FFFFF; // Allows for 1 Meg offset.
+ }
+
+ emuptr resetVector = EmMemGet32 (romStart + bigROMOffset + offsetof (CardHeaderType, resetVector));
+
+ Debug::SetBreakpoint (dbgTempBPIndex, resetVector, NULL);
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::Save
+ *
+ * DESCRIPTION: Standard save function. Saves any sub-system state to
+ * the given session file.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::Save (SessionFile& f)
+{
+ Hordes::Save (f);
+ EmEventPlayback::Save (f);
+ EmPatchMgr::Save (f);
+ Platform_NetLib::Save (f);
+ EmPalmHeap::Save (f);
+ EmLowMem::Save (f);
+
+ const long kCurrentVersion = 2;
+
+ Chunk chunk;
+ EmStreamChunk s (chunk);
+
+ s << kCurrentVersion;
+
+ s << gStackList;
+ s << gBootStack;
+ s << gKernelStack;
+ s << gInterruptStack;
+
+ s << gStackHigh;
+ s << gStackLowWaterMark;
+ s << gStackLowWarn;
+ s << gStackLow;
+
+ s << gKernelStackOverflowed;
+
+ f.WriteStackInfo (chunk);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::Load
+ *
+ * DESCRIPTION: Standard load function. Loads any sub-system state
+ * from the given session file.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::Load (SessionFile& f)
+{
+ Hordes::Load (f);
+ EmEventPlayback::Load (f);
+ EmPatchMgr::Load (f);
+ Platform_NetLib::Load (f);
+ EmPalmHeap::Load (f);
+ EmLowMem::Load (f);
+
+ Chunk chunk;
+ if (f.ReadStackInfo (chunk))
+ {
+ long version;
+ EmStreamChunk s (chunk);
+
+ s >> version;
+
+ if (version >= 1)
+ {
+ s >> gStackList;
+ s >> gBootStack;
+ s >> gKernelStack;
+ s >> gInterruptStack;
+
+ s >> gStackHigh;
+ s >> gStackLowWaterMark;
+ s >> gStackLowWarn;
+ s >> gStackLow;
+ }
+
+ if (version >= 2)
+ {
+ s >> gKernelStackOverflowed;
+ }
+ }
+ else
+ {
+ f.SetCanReload (false); // Need to reboot
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::Dispose
+ *
+ * DESCRIPTION: Standard dispose function. Completely release any
+ * resources acquired or allocated in Initialize and/or
+ * Load.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::Dispose (void)
+{
+ EmLowMem::Dispose ();
+ EmPalmHeap::Dispose ();
+ Platform_NetLib::Dispose ();
+ EmPatchMgr::Dispose ();
+ EmEventPlayback::Dispose ();
+ Hordes::Dispose ();
+}
+
+
+#pragma mark -
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::CheckStackPointerAssignment
+ *
+ * DESCRIPTION: !!! This operation slows down Gremlins by about 10%; it
+ * should be sped up or made an option.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::CheckStackPointerAssignment (void)
+{
+ // If it was a "drastic" change, assume that we're *setting* the
+ // stack pointer to a new stack. Scarf up information about that
+ // block of memory and treat that block as a stack.
+
+ // See if we already know about this stack.
+
+ emuptr curA7 = gCPU->GetSP ();
+ StackList::iterator iter = gStackList.begin ();
+
+ while (iter != gStackList.end ())
+ {
+ if ((curA7 >= (*iter).fBottom) && (curA7 <= (*iter).fTop))
+ {
+ // If so, switch to it.
+ SetCurrentStack (*iter);
+ return;
+ }
+
+ ++iter;
+ }
+
+ // If not, get some information about it and save it off.
+
+ const EmPalmHeap* heap = EmPalmHeap::GetHeapByPtr (curA7);
+ if (heap)
+ {
+ const EmPalmChunk* chunk = heap->GetChunkBodyContaining (curA7);
+ if (chunk)
+ {
+ // The second time we switch stacks, we're switching from the
+ // boot stack to the kernel stack. In that case, forget any
+ // notion of the boot stack.
+
+ if (gBootStack.fBottom != EmMemNULL)
+ {
+ ForgetStack (gBootStack.fBottom);
+ }
+
+ // See if this looks like the AMX kernel stack.
+ // We detect this by seeing (a) if the stack pointer
+ // is being set to a value not too close to the end
+ // of the chunk it's in (indicating that the stack is
+ // embedded in some other data, as is the AMX stack)
+ // and (b) if the values the stack pointer points to
+ // are the appropriate tags.
+
+ CEnableFullAccess munge;
+ if (curA7 <= (chunk->BodyEnd () - 8) &&
+ EmMemGet32 (curA7) == CJ_TAGAMX &&
+ EmMemGet32 (curA7 + 4) == CJ_TAGFENCE)
+ {
+ RememberKernelStack ();
+
+ // Remember this stack as the "current" stack.
+
+ SetCurrentStack (gKernelStack);
+ }
+ else
+ {
+ RememberStackChunk (*chunk);
+
+ // Remember this stack as the "current" stack.
+
+ StackRange range (chunk->BodyStart (),
+ chunk->BodyStart () + chunk->BodySize ());
+ SetCurrentStack (range);
+ }
+ }
+ else
+ {
+ // !!!
+ }
+ }
+ else
+ {
+ if (gStackList.size () == 0)
+ {
+ // We're switching from the reset stack to the boot stack.
+
+ RememberBootStack ();
+
+ // Remember this stack as the "current" stack.
+
+ SetCurrentStack (gBootStack);
+ }
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::CheckStackPointerDecrement
+ *
+ * DESCRIPTION: !!! This operation slows down Gremlins by about 10%; it
+ * should be sped up or made an option.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::CheckStackPointerDecrement (void)
+{
+ // If it was an "incremental" change, make sure that A7 is not
+ // now outside the stack it used to be in (as indicated by oldA7).
+
+ // Test for "close to the end" (in which case we display a warning), and
+ // "past the end" (in which case we print a fatal error).
+
+ if (gKernelStackOverflowed)
+ return;
+
+ emuptr curA7 = gCPU->GetSP ();
+ Bool warning = curA7 < gStackLowWarn;
+ Bool fatal = curA7 < gStackLow;
+
+ if (gStackLowWaterMark > curA7)
+ gStackLowWaterMark = curA7;
+
+ if (warning)
+ {
+ // Special hack for the kernel stack overflowing into the
+ // interrupt stack. If there was a stack overflow and the
+ // current stack is the kernel stack, then ignore the error.
+ // Also, try limiting this hack to older Palm OS's by looking
+ // at the stack size. Newer ROMs will have larger stacks and
+ // should not be allowed to have this bug.
+
+ if (fatal && gKernelStack.fTop == gStackHigh &&
+ gKernelStack.fTop - gKernelStack.fBottom <= 0x2F4)
+ {
+ gKernelStackOverflowed = 1;
+ return;
+ }
+
+ if (fatal)
+ {
+ Errors::ReportErrStackOverflow ();
+ }
+ else
+ {
+ EmAssert (gSession);
+ gSession->ScheduleDeferredError (new EmDeferredErrStackFull);
+ }
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::CheckKernelStack
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::CheckKernelStack (void)
+{
+ EmAssert (gKernelStackOverflowed);
+
+ emuptr curA7 = gCPU->GetSP ();
+
+ if (curA7 <= gStackHigh && curA7 >= gStackLow)
+ {
+ gKernelStackOverflowed = 0;
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::RememberStackChunk
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::RememberStackChunk (const EmPalmChunk& chunk)
+{
+ // Create a range object for the chunk.
+
+ StackRange range (chunk.BodyStart (), chunk.BodyEnd ());
+
+ // Remember it for later.
+
+ RememberStackRange (range);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::RememberBootStack
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::RememberBootStack (void)
+{
+ /*
+ There's a constant in SystemPrv.h that indicates that the boot
+ stack should be 4K long. However, that 4K is relative to the
+ start of the dynamic heap, which is used while the boot stack
+ is in effect. As the system boots up, blocks are allocated and
+ de-allocated in the stack range. We have a check in ForgetStack
+ that checks to see if a MemFooFree operation is taking place
+ within a stack -- normally a dubious operation -- and it's
+ noticing this situation.
+
+ For now, it looks like we only use 0x438 bytes* of the boot
+ stack, so I'll allow 0x500. This should prevent the overlap of
+ the boot stack with any allocated chunks.
+
+ * This was against one particular ROM, but I don't remember
+ which. Checking a Palm IIIc 4.0 Debug ROM just now, I see
+ it using 0x308 bytes.
+ */
+
+// const int kBootStackSize = 0x1000; // Per SystemPrv.h
+ const int kBootStackSize = 0x0500; // Per experimentation
+
+ gBootStack = StackRange (
+ gCPU->GetSP () - kBootStackSize,
+ gCPU->GetSP ());
+
+ RememberStackRange (gBootStack);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::RememberKernelStack
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::RememberKernelStack (void)
+{
+ // Scan down for the bottom of stack marker.
+
+ CEnableFullAccess munge;
+
+ emuptr ksp = gCPU->GetSP ();
+ while (EmMemGet32 (ksp) != CJ_TAGFENCE)
+ ksp -= 2;
+
+ emuptr isp = ksp - 4;
+ while (EmMemGet32 (isp) != CJ_TAGFENCE)
+ isp -= 2;
+
+ // Add the two stacks. Add the interrupt stack first, and the
+ // kernel stack second. That way, the last added stack -- the
+ // kernel stack -- is remembered as the "current" stack.
+
+ gInterruptStack = StackRange (isp + 4, ksp);
+ RememberStackRange (gInterruptStack);
+
+ gKernelStack = StackRange (ksp + 4, gCPU->GetSP ());
+ RememberStackRange (gKernelStack);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::RememberStackRange
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::RememberStackRange (const StackRange& range)
+{
+#if _DEBUG
+ // Make sure this range doesn't overlap any other stack in our list.
+
+ StackList::iterator iter = gStackList.begin ();
+
+ while (iter != gStackList.end ())
+ {
+ if (range.fBottom >= iter->fBottom && range.fBottom < iter->fTop)
+ EmAssert (false);
+
+ if (range.fTop > iter->fBottom && range.fTop <= iter->fTop)
+ EmAssert (false);
+
+ ++iter;
+ }
+#endif
+
+ // Add the range to the list.
+
+ gStackList.push_back (range);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::SetCurrentStack
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::SetCurrentStack (const StackRange& range)
+{
+ // Record the low-water mark of the previous stack.
+
+ StackList::iterator iter = gStackList.begin ();
+
+ while (iter != gStackList.end ())
+ {
+ if (gStackHigh == iter->fTop)
+ {
+ iter->fLowWaterMark = gStackLowWaterMark;
+
+ if (gStackHigh == gBootStack.fTop)
+ gBootStack.fLowWaterMark = gStackLowWaterMark;
+
+ if (gStackHigh == gKernelStack.fTop)
+ gKernelStack.fLowWaterMark = gStackLowWaterMark;
+
+ if (gStackHigh == gInterruptStack.fTop)
+ gInterruptStack.fLowWaterMark = gStackLowWaterMark;
+
+ break;
+ }
+
+ ++iter;
+ }
+
+ // Determine the amount to test against when determining if we are
+ // close to overflowing the stack. For applications, let's set this
+ // to 50 bytes. For the kernel stack, set it to zero; some Palm OS's
+ // get damn close to the end of the kernel stack (the Palm VII ROM gets
+ // within 8 bytes!).
+
+ const int kAppStackSlush = 50;
+ const int kKernelStackSlush = 0;
+
+ int stackSlush = kAppStackSlush;
+
+ if (range == gKernelStack)
+ stackSlush = kKernelStackSlush;
+
+ gStackHigh = range.fTop;
+ gStackLowWaterMark = range.fLowWaterMark;
+ gStackLowWarn = range.fBottom + kInterruptOverhead + stackSlush;
+ gStackLow = range.fBottom + kInterruptOverhead;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::ForgetStack
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::ForgetStack (emuptr stackBottom)
+{
+ StackList::iterator iter = gStackList.begin ();
+
+ while (iter != gStackList.end ())
+ {
+ // If the pointer is in the *middle* of a stack, that's not good.
+
+ if (stackBottom > iter->fBottom && stackBottom < iter->fTop)
+ EmAssert (false);
+
+ // If the pointer is to the beginning of the stack, we've found
+ // the one we want to remove.
+
+ if (stackBottom == iter->fBottom)
+ {
+ gStackList.erase (iter);
+
+ if (stackBottom == gBootStack.fBottom)
+ {
+#define TRACK_BOOT_ALLOCATION 0
+#if TRACK_BOOT_ALLOCATION
+ LogAppendMsg ("===== Forgetting Boot Stack, top = 0x%08X, low water mark: 0x%08X =====",
+ gBootStack.fTop, gBootStack.fLowWaterMark);
+#endif
+
+ gBootStack = StackRange ();
+ }
+
+ return;
+ }
+
+ ++iter;
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::ForgetStacksIn
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::ForgetStacksIn (emuptr start, uint32 range)
+{
+ StackList::iterator iter = gStackList.begin ();
+
+ while (iter != gStackList.end ())
+ {
+ if ((iter->fBottom >= start) && (iter->fTop <= (start + range)))
+ {
+ gStackList.erase (iter);
+
+ if (start == gBootStack.fBottom)
+ {
+#define TRACK_BOOT_ALLOCATION 0
+#if TRACK_BOOT_ALLOCATION
+ LogAppendMsg ("===== Forgetting Boot Stack, top = 0x%08X, low water mark: 0x%08X =====",
+ gBootStack.fTop, gBootStack.fLowWaterMark);
+#endif
+
+ gBootStack = StackRange ();
+ }
+
+ // We munged the collection; start over.
+
+ iter = gStackList.begin ();
+ continue;
+ }
+
+ ++iter;
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::GetBootStack
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+StackRange EmPalmOS::GetBootStack (void)
+{
+ return gBootStack;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::IsInStack
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+Bool EmPalmOS::IsInStack (emuptr addr)
+{
+ return addr >= gStackLow && addr < gStackHigh;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::IsInStackBlock
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+Bool EmPalmOS::IsInStackBlock (emuptr addr)
+{
+ return addr >= gCPU->GetSP () && addr < gStackHigh;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::GenerateStackCrawl
+ *
+ * DESCRIPTION: Starting with the current PC and A6, generate a list
+ * of active functions.
+ *
+ * PARAMETERS: frameList - reference to the collection to receive
+ * the results.
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void EmPalmOS::GenerateStackCrawl (EmStackFrameList& frameList)
+{
+ // Make sure we have access to all of memory, in case A6 is bogus
+ // or used for some purpose other than frame links.
+
+ CEnableFullAccess munge;
+
+ // Clear out the stack crawl list.
+
+ frameList.clear ();
+
+ // Push the initial stack frame onto the stack. This consists
+ // of the current PC and A6 values.
+
+ EmStackFrame frame;
+
+ frame.fAddressInFunction = m68k_getpc ();
+ frame.fA6 = m68k_areg (regs, 6);
+
+ frameList.push_back (frame);
+
+ // If A6 is odd or not in the current stack, stop the stack crawl.
+
+ if (::IsOdd (frame.fA6) || !EmPalmOS::IsInStack (frame.fA6))
+ return;
+
+ while (1)
+ {
+ emuptr oldA6 = frame.fA6;
+
+ // Get the previous A6 and function from the stack.
+
+ frame.fAddressInFunction = EmMemGet32 (oldA6 + 4);
+ frame.fA6 = EmMemGet32 (oldA6);
+
+ // If A6 is odd or not in the current stack, stop the stack crawl.
+
+ if (::IsOdd (frame.fA6) || !EmPalmOS::IsInStack (frame.fA6))
+ return;
+
+ // If the new A6 is not greater than the old A6, stop
+ // the stack crawl.
+
+ if (frame.fA6 <= oldA6)
+ return;
+
+ // If the return address is not valid, let's take a chance
+ // that what's on the stack is <A6> <SR> <return address>.
+ // We'd see this sequence if an exception has occurred and
+ // and exception frame is on the stack.
+
+ if (!EmMemCheckAddress (frame.fAddressInFunction, 2))
+ {
+ frame.fAddressInFunction = EmMemGet32 (oldA6 + 6);
+
+ // If the return address still doesn't look valid,
+ // stop the stack crawl.
+
+ if (!EmMemCheckAddress (frame.fAddressInFunction, 2))
+ {
+ return;
+ }
+ }
+
+ // Everything looks OK, push this information onto our stack.
+
+ frameList.push_back (frame);
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmPalmOS::PrefsChanged
+// ---------------------------------------------------------------------------
+// Respond to a preference change.
+
+static void PrvRespondToPrefsChange (PrefKeyType prefKey)
+{
+ if (EmPatchState::UIInitialized ())
+ {
+ if (::PrefKeysEqual (prefKey, kPrefKeyReportStrictIntlChecks) && EmPatchMgr::IntlMgrAvailable ())
+ {
+ Preference<Bool> pref (kPrefKeyReportStrictIntlChecks, false);
+ ::IntlSetStrictChecks (*pref);
+ }
+
+ if (::PrefKeysEqual (prefKey, kPrefKeyReportOverlayErrors))
+ {
+ Preference<Bool> pref (kPrefKeyReportOverlayErrors, false);
+ (void) ::FtrSet (omFtrCreator, omFtrShowErrorsFlag, *pref);
+ }
+ }
+}
+
+
+void EmPalmOS::PrefsChanged (PrefKeyType prefKey, void*)
+{
+ if (gSession)
+ {
+#if HAS_OMNI_THREAD
+ if (gSession->InCPUThread ())
+ {
+ ::PrvRespondToPrefsChange (prefKey);
+ }
+ else
+#endif
+ {
+ EmSessionStopper stopper (gSession, kStopOnSysCall);
+
+ ::PrvRespondToPrefsChange (prefKey);
+ }
+ }
+}
+
+
+#pragma mark -
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::HandleTrap15
+ *
+ * DESCRIPTION: Handle a trap. Traps are of the format TRAP $F / $Axxx.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+Bool EmPalmOS::HandleTrap15 (ExceptionNumber)
+{
+ return EmPalmOS::HandleSystemCall (true);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Software::HandleJSR
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+Bool EmPalmOS::HandleJSR (emuptr oldpc, emuptr dest)
+{
+#if HAS_PROFILING
+ if (gProfilingEnabled)
+ {
+ StDisableMemoryCycleCounting stopper;
+ Boolean skipProfiling = false;
+
+ // In multi-segmented application generated by CodeWarrior,
+ // inter-segment function calls are implemented by JSR'ing
+ // to a jump table entry. This jump table entry contains
+ // a JMP to the final location. Detect this case and emit
+ // a function entry record for the final location, not the
+ // jump table entry.
+
+ const uint16 kOpcode_JMP_Abs32 = 0x4EF9;
+ uint8* realMem = EmMemGetRealAddress (dest);
+
+ if (EmMemDoGet16 (realMem) == kOpcode_JMP_Abs32)
+ {
+ dest = EmMemDoGet32 (realMem + 2);
+ }
+ else
+ {
+ //---------------------------------------------------------------------
+ // We don't want to process JSRs to a switch statement. Currently
+ // CodeWarrior generates a switch statement block like this:
+ // MOVE.L (A7)+,A0 | 205F
+ // MOVE.L A0,A1 | 2248
+ // ADD.W (A0)+,A1 | D2D8
+ // CMP.W (A0)+,D0 | B058
+ // BGE.S *+$0004 ; 10C20F5C | 6C02
+ // JMP (A1) | 4ED1
+ // CMP.W (A0)+,D0 | B058
+ // BLE.S *+$0004 ; 10C20F62 | 6F02
+ // JMP (A1) | 4ED1
+ // MOVE.W (A0)+,D1 | 3218
+ // CMP.W (A0)+,D0 | B058
+ // BNE.S *+$0006 ; 10C20F6C | 6604
+ // ADD.W (A0),A0 | D0D0
+ // JMP (A0) | 4ED0
+ // ADDQ.W #$02,A0 | 5448
+ // DBF D1,*-$000A ; 10C20F64 | 51C9 FFF4
+ // JMP (A1) | 4ED1
+ //---------------------------------------------------------------------
+ skipProfiling = (EmMemDoGet32 (realMem) == 0x205F2248)
+ && (EmMemDoGet32 (realMem + 4) == 0xD2D8B058)
+ && (EmMemDoGet32 (realMem + 8) == 0x6C024ED1);
+ }
+
+ if (!skipProfiling)
+ {
+ ProfileFnEnter (dest, oldpc);
+ }
+ }
+#endif
+
+#if LOG_FUNCTION_CALLS
+// char fromName[80];
+ char toName[80];
+
+// ::FindFunctionName (oldpc, fromName, NULL, NULL, 80);
+ ::FindFunctionName (dest, toName, NULL, NULL, 80);
+
+ EmStackFrameList stackCrawl;
+ EmPalmOS::GenerateStackCrawl (stackCrawl);
+
+ string dots ("|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-");
+ dots.resize (stackCrawl.size ());
+
+// LogAppendMsg (">>> %s --> %s", fromName, toName);
+// LogAppendMsg ("--- System Call 0xA08D: %s", fromName, toName);
+ LogAppendMsg ("--- Function Call: %s%s", dots.c_str (), toName);
+#endif
+
+ return false; // We didn't completely handle it; do default handling.
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::HandleJSR_Ind
+ *
+ * DESCRIPTION: Check for SYS_TRAP_FAST calls.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+Bool EmPalmOS::HandleJSR_Ind (emuptr oldpc, emuptr dest)
+{
+ Bool handledIt = false; // Default to calling ROM.
+
+ // inline asm SysTrapFast(Int trapNum)
+ // {
+ // MOVE.L struct(LowMemType.fixed.globals.sysDispatchTableP), A1
+ // MOVE.L ((trapNum-sysTrapBase)*4)(A1), A1
+ // JSR (A1) // call it
+ // }
+ //
+ // #define SYS_TRAP_FAST(trapNum)
+ // FIVEWORD_INLINE(
+ // 0x2278, 0x0122,
+ // 0x2269, (trapNum-sysTrapBase)*4,
+ // 0x4e91)
+
+ uint8* realMem = EmMemGetRealAddress (oldpc);
+ if (EmMemDoGet16 (realMem) == 0x4e91 &&
+ EmMemDoGet16 (realMem - 4) == 0x2269 &&
+ EmMemDoGet16 (realMem - 8) == 0x2278)
+ {
+ handledIt = EmPalmOS::HandleSystemCall (false);
+ }
+ else
+ {
+ if (gBigROMEntry == EmMemNULL)
+ {
+ emuptr base = EmBankROM::GetMemoryStart ();
+
+ // Check every romDelta (4K) for up to maxBigROMOffset
+ //(256K) till we find the "Big" ROM
+
+ const UInt32 romDelta = 4 * 1024L; // BigROM starts on a 4K boundary
+ const UInt32 maxBigROMOffset = 256 * 1024L; // Give up looking past here
+ UInt16 loops = maxBigROMOffset / romDelta; // How many loops to do
+ emuptr bP = base + romDelta;
+
+ while (loops--)
+ {
+ // Ignore older card headers that might have been
+ // programmed in at a lower address (hdrVersion < 3)
+
+ EmAliasCardHeaderType<PAS> cardHdr (bP);
+
+ UInt32 signature = cardHdr.signature;
+ UInt16 hdrVersion = cardHdr.hdrVersion;
+
+ if (signature == sysCardSignature && hdrVersion >= 3)
+ {
+ gBigROMEntry = cardHdr.resetVector;
+ break;
+ }
+
+ bP += romDelta;
+ }
+
+ // See if we found it (may have hdrVersion < 3)
+
+ if (gBigROMEntry == EmMemNULL)
+ {
+ EmAliasCardHeaderType<PAS> smallCardHdr (base);
+ UInt32 bigROMOffset;
+
+ if (smallCardHdr.hdrVersion == 2)
+ {
+ bigROMOffset = smallCardHdr.bigROMOffset & 0x000FFFFF;
+ }
+ else
+ {
+ bigROMOffset = 0x3000;
+ }
+
+ EmAliasCardHeaderType<PAS> bigCardHdr (base + bigROMOffset);
+ gBigROMEntry = bigCardHdr.resetVector;
+ }
+ }
+
+ if (dest == gBigROMEntry)
+ {
+ EmAssert (gSession);
+ gSession->ScheduleReset (kResetSys);
+ }
+ }
+
+ return handledIt;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::HandleLINK
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::HandleLINK (int /*linkSize*/)
+{
+#if 0
+ Preference<bool> pref (kPrefKeyFillStack);
+ if (*pref && linkSize < 0)
+ {
+ uae_memset (gCPU->GetSP (), 0xD5, -linkSize);
+ }
+#endif
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::HandleRTS
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+Bool EmPalmOS::HandleRTS (emuptr dest)
+{
+ UNUSED_PARAM (dest);
+
+#if HAS_PROFILING
+ if (gProfilingEnabled)
+ {
+ StDisableMemoryCycleCounting stopper;
+
+ ProfileFnExit (dest, gCPU->GetPC () + 2);
+ }
+#endif
+
+#if LOG_FUNCTION_CALLS
+ char fromName[80];
+ char toName[80];
+
+ ::FindFunctionName (gCPU->GetPC(), fromName, NULL, NULL, 80);
+ ::FindFunctionName (dest, toName, NULL, NULL, 80);
+
+ LogAppendMsg ("<<< %s --> %s", fromName, toName);
+#endif
+
+#if 0
+ char fromName[80];
+ ::FindFunctionName (gCPU->GetPC(), fromName, NULL, NULL, 80);
+
+ if (strcmp (fromName, "HwrADC") == 0)
+ {
+ // A7 points to return address
+ // A7 + 4 points to UInt16 (command)
+ // A7 + 6 points to pointer to result
+
+ enum hwrADCCmdEnum
+ {
+ hwrADCCmdReadXYPos=0, // Read Digitizer X & Y Positions
+ hwrADCCmdReadBatt, // Read Battery Level
+ hwrADCCmdReadAux, // Read Battery Level
+ hwrADCCmdReadTemp, // Read Temperature
+ hwrADCCmdReadXPos, // Read Digitizer X Position
+ hwrADCCmdReadYPos, // Read Digitizer Y Position
+ hwrADCCmdReadZPos, // Read Digitizer Z Position (Pressure)
+ hwrADCCmdReadXYZPos, // Read Digitizer X, Y & Z Positions
+ hwrADCCmdPenIRQOn // Enable Pen IRQ
+ } ;
+
+ struct HwrADCReadResultType
+ {
+ UInt16 reserved1; //mVolts; // level in millivolts (2500 = 2.5 volts)
+ UInt16 abs; // absolute level (0 -> 255)
+ UInt16 aux; // absolute level (0 -> 255)
+ };
+
+ const char* text[] =
+ {
+ "hwrADCCmdReadXYPos",
+ "hwrADCCmdReadBatt",
+ "hwrADCCmdReadAux",
+ "hwrADCCmdReadTemp",
+ "hwrADCCmdReadXPos",
+ "hwrADCCmdReadYPos",
+ "hwrADCCmdReadZPos",
+ "hwrADCCmdReadXYZPos",
+ "hwrADCCmdPenIRQOn",
+ "unknown"
+ };
+
+ UInt16 cmd = EmMemGet16 (gCPU->GetSP () + 4);
+ emuptr ptr = EmMemGet32 (gCPU->GetSP () + 6);
+ UInt16 reserved1 = EmMemGet16 (ptr + 0);
+ UInt16 abs = EmMemGet16 (ptr + 2);
+ UInt16 aux = EmMemGet16 (ptr + 4);
+
+ LogAppendMsg ("HwrADC (%s), reserved1 = 0x%04X, abs = 0x%04X, aux = 0x%04X",
+ text[cmd], (int) reserved1, (int) abs, (int) aux);
+ }
+ else if (strcmp (fromName, "HwrDockStatus") == 0)
+ {
+ UInt16 result = m68k_dreg (regs, 0);
+ LogAppendMsg ("HwrDockStatus result = 0x%04X",(int) result);
+ }
+#endif
+
+ return false; // We didn't completely handle it; do default handling.
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::HandleRTE
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+Bool EmPalmOS::HandleRTE (emuptr dest)
+{
+ UNUSED_PARAM (dest);
+
+#if HAS_PROFILING
+ if (gProfilingEnabled)
+ {
+ ProfileInterruptExit (dest);
+ }
+#endif
+
+#if LOG_FUNCTION_CALLS
+ char fromName[80];
+ char toName[80];
+
+ ::FindFunctionName (gCPU->GetPC(), fromName, NULL, NULL, 80);
+ ::FindFunctionName (dest, toName, NULL, NULL, 80);
+
+ LogAppendMsg ("<<< %s --> %s", fromName, toName);
+#endif
+
+ return false; // We didn't completely handle it; do default handling.
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::HandleNewPC
+ *
+ * DESCRIPTION: The PC is about to be changed via a JSR, BSR, RTS, or
+ * RTE. Check that the new address appears to be valid.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::HandleNewPC (emuptr dest)
+{
+ // If the address is odd, then it's bad.
+
+ if ((dest & 1) != 0)
+ Errors::ReportErrInvalidPC (dest, kOddAddress);
+
+// if (!EmPatchState::UIInitialized ())
+// return;
+
+ // Ensure that the PC is in ROM or RAM.
+
+ EmMemPutFunc longSetter = EmMemGetBank (dest).lput;
+
+ if (longSetter == EmBankROM::SetLong || longSetter == EmBankFlash::SetLong)
+ {
+ if (!EmBankROM::ValidAddress (dest, 2))
+ {
+ Errors::ReportErrInvalidPC (dest, kNotInCodeSegment);
+ }
+ }
+ else if (longSetter == EmBankSRAM::SetLong)
+ {
+ }
+ else if (longSetter == EmBankDRAM::SetLong)
+ {
+ if (dest < (256 + 693 * 4) /*MetaMemory::GetSysGlobalsEnd ()*/)
+ {
+ Errors::ReportErrInvalidPC (dest, kNotInCodeSegment);
+ }
+ }
+ else if (longSetter == EmBankMapped::SetLong)
+ {
+ if (!EmBankMapped::ValidAddress (dest, 2))
+ {
+ Errors::ReportErrInvalidPC (dest, kUnmappedAddress);
+ }
+ }
+ else
+ {
+ Errors::ReportErrInvalidPC (dest, kUnmappedAddress);
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::HandleNewSP
+ *
+ * DESCRIPTION: !!! This operation slows down Gremlins by about 10%; it
+ * should be sped up or made an option.
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void EmPalmOS::HandleNewSP (EmStackChangeType type)
+{
+ if (type == kStackPointerChanged)
+ {
+ CheckStackPointerAssignment ();
+ }
+ else if (type == kStackPointerDecremented)
+ {
+ CheckStackPointerDecrement ();
+ }
+ else if (type == kStackPointerKernelStackHack)
+ {
+ CheckKernelStack ();
+ }
+}
+
+
+#pragma mark -
+
+/***********************************************************************
+ *
+ * FUNCTION: EmPalmOS::HandleSystemCall
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+Bool EmPalmOS::HandleSystemCall (Bool fromTrap)
+{
+ // ======================================================================
+ // First things first: if we need to break execution on the next call
+ // to a system function, make sure that happens.
+ // ======================================================================
+
+ EmAssert (gSession);
+ EmAssert (gCPU68K);
+
+#if HAS_PROFILING
+ int oldProfilingCounted = gProfilingCounted;
+ gProfilingCounted = false;
+#endif
+
+ // If the system call is being made by a TRAP $F, the PC has already
+ // been bumped past the opcode. If being made with a JSR via the
+ // SYS_TRAP_FAST macro, the PC has not been adjusted. Determine a
+ // "pcAdjust" value that allows us to get back to the start of the
+ // instruction that got us here.
+
+ int pcAdjust = fromTrap ? 2 : 0;
+
+ if (!gSession->IsNested () && gSession->GetBreakOnSysCall ())
+ {
+ CEnableFullAccess munge;
+
+ // Check the memory manager semaphore. If we're stopping on a
+ // system call, it's most likely that we want to make some nested
+ // Palm OS calls. Some of those calls may try to acquire the
+ // memory manager semaphore. Therefore, make sure it's available.
+
+ UInt32 memSemaphoreIDP = EmLowMem_GetGlobal (memSemaphoreID);
+ EmAliascj_xsmb<PAS> memSemaphoreID (memSemaphoreIDP);
+
+ if (memSemaphoreID.xsmuse == 0)
+ {
+ // Check the current task. We don't want to break on a system
+ // call if we're in the background. If we're breaking on a
+ // system call, it's likely because some other part of Poser
+ // wants to make OS calls. Occassionally, those OS calls result
+ // in trying to switch to the UI task (see, for example,
+ // PrvEditCardOptionsOK). If we're already in the UI task, then
+ // that's OK, but if we're in a background task, an attempted
+ // switch to the UI task will fail, due to Poser turning off
+ // interrupts when it makes OS calls.
+
+ SysKernelInfoType taskInfo;
+ taskInfo.selector = sysKernelInfoSelCurTaskInfo;
+
+ Err err = ::SysKernelInfo (&taskInfo);
+ if (err == errNone)
+ {
+ if (taskInfo.param.task.tag == 'psys')
+ {
+ gCPU->SetPC (gCPU->GetPC () - pcAdjust);
+ gSession->ScheduleSuspendSysCall ();
+
+#if HAS_PROFILING
+ gProfilingCounted = oldProfilingCounted;
+#endif
+
+ // Return true to say that everything has been handled.
+
+ return true;
+ }
+
+ // If we took a stab at this and didn't find that we were
+ // in the UI task, then call EvtWakeup. If our context is
+ // one where an application has called EvtGetEvent with a
+ // "sleep forever" timeout, then the current task will be
+ // 'AMX ', and will stay that way. Calling EvtWakeup will
+ // force an eventual switch to the UI task.
+ //
+ // Note that we wake-up the UI thread only if we're in the
+ // root AMX thread. This prevents us from trying to call
+ // it when the Palm context is one where a background task
+ // is the current task. It's possible for that task to be
+ // sleeping or something (the example we ran into was with
+ // a background task calling SysDelay). EvtWakeup will try
+ // to switch to the UI task, but will not return until the
+ // UI task switches back to the background task. That will
+ // never happen because interrupts are turned off during
+ // all Poser calls into the OS (such as the call to
+ // EvtWakeup). Thus, the only safe places to call
+ // EvtWakeup are in the UI or AMX tasks.
+
+ else if (taskInfo.param.task.tag == 0x414d5800)
+ {
+ ::EvtWakeup ();
+ }
+ }
+ }
+ }
+
+
+ // ======================================================================
+ // Determine what ROM function is about to be called, and determine
+ // the method by which it is being called.
+ // ======================================================================
+
+ SystemCallContext context;
+ Bool gotFunction = GetSystemCallContext (
+ gCPU->GetPC () - pcAdjust, context);
+
+
+ // ======================================================================
+ // Validate the address for the ROM function we're about to call.
+ // ======================================================================
+
+ if (!gotFunction)
+ {
+#if HAS_PROFILING
+ gProfilingCounted = oldProfilingCounted;
+#endif
+
+ if (context.fError == kError_UnimplementedTrap)
+ {
+ Errors::ReportErrUnimplementedTrap (context);
+ }
+ else if (context.fError == kError_InvalidLibraryRefNum)
+ {
+ Errors::ReportErrInvalidRefNum (context);
+ }
+
+ // We should never get here. context.fError should always equal
+ // one of those two error codes, and those two functions we call
+ // should never return (they should throw exceptions).
+
+ EmAssert (false);
+ }
+
+
+ // ======================================================================
+ // Record what function we're calling.
+ // ======================================================================
+
+ if (!gSession->IsNested () && LogSystemCalls ())
+ {
+ char name [sysPktMaxNameLen];
+
+ strcpy (name, ::GetTrapName (context, true));
+
+ EmStackFrameList stackCrawl;
+ EmPalmOS::GenerateStackCrawl (stackCrawl);
+
+ string dots ("|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-");
+ dots.resize (stackCrawl.size ());
+
+ LogAppendMsg ("--- System Call 0x%04X: %s%s.", (long) context.fTrapWord, dots.c_str (), name);
+ }
+
+
+ // ======================================================================
+ // Let the debugger have a crack at it. It may want to do a "break
+ // on ATrap" thingy. If so, it will return true. Otherwise,
+ // if no action is necessary, it will return false.
+ // ======================================================================
+
+ if (!gSession->IsNested () && Debug::HandleSystemCall (context))
+ {
+ // Return true to say that everything has been handled.
+
+#if HAS_PROFILING
+ gProfilingCounted = oldProfilingCounted;
+#endif
+
+ return true;
+ }
+
+
+ // ======================================================================
+ // Check for functions unsupported on ARM. ProscribedFunction
+ // returns true if function is not supported.
+ // ======================================================================
+
+ if ((Memory::IsPCInRAM () && !MetaMemory::InRAMOSComponent (context.fPC))
+ && ::ProscribedFunction (context))
+ {
+ gSession->ScheduleDeferredError (
+ new EmDeferredErrProscribedFunction (context));
+ }
+
+
+ // ======================================================================
+ // Tell the profiler that we're entering a function.
+ // ======================================================================
+#if HAS_PROFILING
+ // This ProfileFnEnter will show the function call within the trap handler
+ // exception, which gives us a nice breakdown of which traps are being
+ // called and frequency.
+
+ if (gProfilingEnabled && context.fViaTrap)
+ {
+ ProfileFnEnter (context.fTrapWord, context.fNextPC);
+ }
+#endif
+
+
+ // ======================================================================
+ // If this trap is patched, let the patch handler handle the patch.
+ // ======================================================================
+
+ CallROMType result = EmPatchMgr::HandleSystemCall (context);
+
+
+ // ======================================================================
+ // If we completely handled the function in head and tail patches, tell
+ // the profiler that we exited the function and get out of here.
+ // ======================================================================
+
+ if (result == kSkipROM)
+ {
+#if HAS_PROFILING
+ if (gProfilingEnabled)
+ {
+ ProfileFnExit (context.fNextPC, context.fTrapWord);
+ }
+#endif
+
+ // Set the PC to point past the instructions that got us here.
+
+ gCPU->SetPC (context.fNextPC);
+
+ // Return true to say that everything has been handled.
+
+#if HAS_PROFILING
+ gProfilingCounted = oldProfilingCounted;
+#endif
+
+ return true;
+ }
+
+
+ // ======================================================================
+ // If we're profiling, don't dispatch to the ROM function outselves.
+ // We want the ROM to do it so that we get accurate dispatch times.
+ // ======================================================================
+
+#if HAS_PROFILING
+ // Don't do native dispatching if the profiler is on!
+
+ if (gProfilingEnabled)
+ {
+ // Return false to do default exception handler processing.
+
+ gProfilingCounted = oldProfilingCounted;
+
+ return false;
+ }
+#endif
+
+#if HAS_PROFILING
+ gProfilingCounted = oldProfilingCounted;
+#endif
+
+ // ======================================================================
+ // Otherwise, let's run the trap dispatcher native. This gives
+ // us about a 10-20% speed-up.
+ // ======================================================================
+
+ // Push the return address onto the stack. Subtract 4 from the stack,
+ // and then store the appropriate return address.
+
+ gCPU->SetSP (gCPU->GetSP () - 4);
+ CHECK_STACK_POINTER_DECREMENT ();
+ EmMemPut32 (gCPU->GetSP (), context.fNextPC);
+
+ // Change to the new PC. If possible, use the fully dereferenced destination
+ // address. This speeds things up for libraries and sub-dispatched managers
+ // (like Floating Point, International, Text, and Overlay managers).
+
+ emuptr destPC = context.fDestPC2;
+ if (Debug::BreakpointInstalled ())
+ {
+ destPC = context.fDestPC1;
+ }
+
+ EmPalmOS::HandleNewPC (destPC);
+ gCPU->SetPC (destPC);
+
+ // Return true to say that everything has been handled.
+
+ return true;
+}
+
+
+
+
+EmStream& operator << (EmStream& s, const StackRange& range)
+{
+ s << range.fBottom;
+ s << range.fTop;
+ s << range.fLowWaterMark;
+
+ return s;
+}
+
+EmStream& operator >> (EmStream& s, StackRange& range)
+{
+ s >> range.fBottom;
+ s >> range.fTop;
+ s >> range.fLowWaterMark;
+
+ return s;
+}