aboutsummaryrefslogtreecommitdiff
path: root/SrcShared/Hardware/EmCPU68K.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SrcShared/Hardware/EmCPU68K.cpp')
-rw-r--r--SrcShared/Hardware/EmCPU68K.cpp1849
1 files changed, 1849 insertions, 0 deletions
diff --git a/SrcShared/Hardware/EmCPU68K.cpp b/SrcShared/Hardware/EmCPU68K.cpp
new file mode 100644
index 0000000..e3f00e2
--- /dev/null
+++ b/SrcShared/Hardware/EmCPU68K.cpp
@@ -0,0 +1,1849 @@
+/* -*- 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 "EmCPU68K.h"
+
+#include "Byteswapping.h" // Canonical
+#include "DebugMgr.h" // gExceptionAddress, gExceptionSize, gExceptionForRead
+#include "EmBankROM.h" // EmBankROM::GetMemoryStart
+#include "EmEventPlayback.h" // EmEventPlayback::ReplayingEvents
+#include "EmHAL.h" // EmHAL::GetInterruptLevel
+#include "EmMemory.h" // CEnableFullAccess
+#include "EmMinimize.h" // IsOn
+#include "EmSession.h" // HandleInstructionBreak
+#include "Logging.h" // LogAppendMsg
+#include "MetaMemory.h" // IsCPUBreak
+#include "Platform.h" // GetMilliseconds
+#include "SessionFile.h" // WriteDBallRegs, etc.
+#include "StringData.h" // kExceptionNames
+#include "UAE.h" // cpuop_func, etc.
+
+#include <algorithm> // find
+
+#if __profile__
+#include <Profiler.h>
+#endif
+
+#if defined (macintosh) && defined (_DEBUG) && 0
+ #define HAS_DEAD_MANS_SWITCH 1
+#else
+ #define HAS_DEAD_MANS_SWITCH 0
+#endif
+
+// !!! Why is this here? Shouldn't it be in Profiling.h?
+#if HAS_PROFILING
+perfRec perftbl[65536];
+#endif
+
+// Define our own flags for regs.spcflag. Please do not let these
+// overlap with UAE-defined flags (should not fall below 0x0002000)
+// and avoid using the high bit just for safety.
+
+#define SPCFLAG_END_OF_CYCLE (0x40000000)
+
+
+// Data needed by UAE.
+
+int areg_byteinc[] = { 1,1,1,1,1,1,1,2 }; // (normally in newcpu.c)
+int imm8_table[] = { 8,1,2,3,4,5,6,7 }; // (normally in newcpu.c)
+
+int movem_index1[256]; // (normally in newcpu.c)
+int movem_index2[256]; // (normally in newcpu.c)
+int movem_next[256]; // (normally in newcpu.c)
+
+cpuop_func* cpufunctbl[65536]; // (normally in newcpu.c)
+
+uint16 last_op_for_exception_3; /* Opcode of faulting instruction */
+emuptr last_addr_for_exception_3; /* PC at fault time */
+emuptr last_fault_for_exception_3; /* Address that generated the exception */
+
+struct regstruct regs; // (normally in newcpu.c)
+struct flag_struct regflags; // (normally in support.c)
+
+
+// These variables should strictly be in a sub-system that implements
+// the stack overflow checking, etc. However, for performance reasons,
+// we need to expose them to UAE (see the CHECK_STACK_POINTER_ASSIGNMENT,
+// et al macros), so define them here.
+//
+// Similar comments for the CheckKernelStack function.
+
+uae_u32 gStackHigh;
+uae_u32 gStackLowWarn;
+uae_u32 gStackLow;
+uae_u32 gKernelStackOverflowed;
+
+
+// Definitions of the stack frames used in EmCPU68K::ProcessException.
+
+#include "PalmPack.h"
+
+struct ExceptionStackFrame1
+{
+ uint16 statusRegister;
+ uint32 programCounter;
+};
+
+struct ExceptionStackFrame2
+{
+ uint16 functionCode;
+ uint32 accessAddress;
+ uint16 instructionRegister;
+ uint16 statusRegister;
+ uint32 programCounter;
+};
+
+#include "PalmPackPop.h"
+
+
+EmCPU68K* gCPU68K;
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::Cycle
+// ---------------------------------------------------------------------------
+
+// this guy is a macro instead of an inline function so that "counter" can
+// be declared as a variable local to the calling function. The resulting
+// code can be more efficient if "counter" can be cached in a register
+// instead of being a static or global variable.
+
+#define CYCLE(sleeping) \
+{ \
+ /* Don't do anything if we're in the middle of an ATrap call. We don't */ \
+ /* need interrupts firing or tmr counters incrementing. */ \
+ \
+ EmAssert (session); \
+ if (!session->IsNested ()) \
+ { \
+ /* Perform CPU-specific idling. */ \
+ \
+ EmHAL::Cycle (sleeping); \
+ \
+ /* Perform expensive operations. */ \
+ \
+ if (sleeping || ((++counter & 0x7FFF) == 0)) \
+ { \
+ this->CycleSlowly (sleeping); \
+ } \
+ } \
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::EmCPU68K
+// ---------------------------------------------------------------------------
+
+EmCPU68K::EmCPU68K (EmSession* session) :
+ EmCPU (session),
+ fLastTraceAddress (EmMemNULL),
+ fCycleCount (0),
+// fExceptionHandlers (),
+ fHookJSR (),
+ fHookJSR_Ind (),
+ fHookLINK (),
+ fHookRTE (),
+ fHookRTS (),
+ fHookNewPC (),
+ fHookNewSP ()
+#if REGISTER_HISTORY
+ , fRegHistoryIndex (0)
+// , fRegHistory ()
+#endif
+#if EXCEPTION_HISTORY
+ , fExceptionHistoryIndex (0)
+// , fExceptionHistory ()
+#endif
+{
+ this->InitializeUAETables ();
+
+ EmAssert (gCPU68K == NULL);
+ gCPU68K = this;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::~EmCPU68K
+// ---------------------------------------------------------------------------
+
+EmCPU68K::~EmCPU68K (void)
+{
+ EmAssert (gCPU68K == this);
+ gCPU68K = NULL;
+}
+
+
+#pragma mark -
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::Reset
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::Reset (Bool hardwareReset)
+{
+ fLastTraceAddress = EmMemNULL;
+ fCycleCount = 0;
+
+#if REGISTER_HISTORY
+ fRegHistoryIndex = 0;
+#endif
+
+#if EXCEPTION_HISTORY
+ fExceptionHistoryIndex = 0;
+#endif
+
+ gStackHigh = EmMemEOM;
+ gStackLowWarn = EmMemNULL;
+ gStackLow = EmMemNULL;
+ gKernelStackOverflowed = false;
+
+ if (hardwareReset)
+ {
+ // (taken from m68k_reset in newcpu.c)
+
+ // !!! I think that we really need to emulate ROM appearing at NULL.
+ // That would break our dependency on EmBankROM.
+
+ emuptr romStart = EmBankROM::GetMemoryStart ();
+ m68k_areg (regs, 7) = EmMemGet32 (romStart);
+ m68k_setpc (EmMemGet32 (romStart + 4));
+
+ // Note, on the 68K, the t0 and m flags must always be zero.
+
+ regs.prefetch = 0x0000;
+ regs.kick_mask = 0xF80000; // (not a 68K register)
+ regs.s = 1; // supervisor/user state
+ regs.m = 0; // master/interrupt state
+ regs.stopped = 0; // (not a 68K register)
+ regs.t1 = 0; // 1 = trace on any instruction
+ regs.t0 = 0; // 1 = trace on change of flow
+ SET_ZFLG (0);
+ SET_XFLG (0);
+ SET_CFLG (0);
+ SET_VFLG (0);
+ SET_NFLG (0);
+ regs.spcflags = 0; // (not a 68K register)
+ regs.intmask = 7; // disable all interrupts
+ regs.vbr = regs.sfc = regs.dfc = 0;
+ regs.fpcr = regs.fpsr = regs.fpiar = 0;
+ }
+
+ Memory::CheckNewPC (m68k_getpc ());
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::Save
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::Save (SessionFile& f)
+{
+ // Write out the CPU Registers
+
+ regstruct tempRegs;
+ this->GetRegisters (tempRegs);
+
+ Canonical (tempRegs);
+ f.WriteDBallRegs (tempRegs);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::Load
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::Load (SessionFile& f)
+{
+ // Read in the CPU Registers.
+
+ regstruct tempRegs;
+ f.ReadDBallRegs (tempRegs);
+
+ Canonical (tempRegs);
+ this->SetRegisters (tempRegs);
+}
+
+
+#pragma mark -
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::Execute
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::Execute (void)
+{
+ // This function is the bottleneck for all 68K emulation. It's
+ // important that it run as quickly as possible. To that end,
+ // fine tune register allocation as much as we can by hand.
+
+#if defined(__powerc) || defined(powerc) || \
+ defined(__powerpc) || defined(powerpc) || \
+ defined(__ppc__) || defined(ppc)
+
+ register int counter = 0;
+ register cpuop_func** functable = cpufunctbl;
+
+ register uint8** pc_p_p = &regs.pc_p;
+ register uint8** pc_meta_oldp_p = &regs.pc_meta_oldp;
+ register uint8** pc_oldp_p = &regs.pc_oldp;
+ register uint32* spcflags_p = &regs.spcflags;
+ register EmSession* session = fSession;
+
+ #define pc_p (*pc_p_p)
+ #define pc_meta_oldp (*pc_meta_oldp_p)
+ #define pc_oldp (*pc_oldp_p)
+ #define spcflags (*spcflags_p)
+
+#elif defined(_MSC_VER) && defined(_M_IX86)
+
+ register int counter = 0;
+ register cpuop_func** functable = cpufunctbl;
+
+ #define pc_p (regs.pc_p)
+ #define pc_meta_oldp (regs.pc_meta_oldp)
+ #define pc_oldp (regs.pc_oldp)
+ #define spcflags (regs.spcflags)
+ #define session (fSession)
+
+#else
+
+ register int counter = 0;
+ register cpuop_func** functable = cpufunctbl;
+
+ #define pc_p (regs.pc_p)
+ #define pc_meta_oldp (regs.pc_meta_oldp)
+ #define pc_oldp (regs.pc_oldp)
+ #define spcflags (regs.spcflags)
+ #define session (fSession)
+
+#endif
+
+#if HAS_PROFILING_DEBUG
+ UInt64 readCyclesSaved = 0;
+ UInt64 writeCyclesSaved = 0;
+ UInt64 clockCyclesSaved = 0;
+#endif
+
+#if HAS_DEAD_MANS_SWITCH
+ // -----------------------------------------------------------------------
+ // Put in a little dead-man's switch. If this function doesn't exit for a
+ // long time, let us get into the debugger.
+ // -----------------------------------------------------------------------
+
+ uint32 deadManNow;
+ uint32 deadManStart = Platform::GetMilliseconds ();
+#endif
+
+ // -----------------------------------------------------------------------
+ // Check for the stopped flag before entering the "execute an opcode"
+ // section. It could be that we last exited the loop while still in stop
+ // mode, and we need to wind our way back down to that spot.
+ // -----------------------------------------------------------------------
+
+ if ((spcflags & SPCFLAG_STOP) != 0)
+ goto StoppedLoop;
+
+ while (1)
+ {
+#if REGISTER_HISTORY
+ // -----------------------------------------------------------------------
+ // Save the registers for the post-mortem, but don't record the
+ // instructions we generate when calling the ROM as a subroutine. We want
+ // those to be as transparent as possible. In particular, we don't want
+ // any functions that we call as part of figuring out why a problem
+ // occurred to knock the problem-causing registers off of our array.
+ // -----------------------------------------------------------------------
+
+// if (!session->IsNested ())
+ {
+ ++fRegHistoryIndex;
+ fRegHistory[fRegHistoryIndex & (kRegHistorySize - 1)] = regs;
+ }
+#endif
+
+#if HAS_DEAD_MANS_SWITCH
+ // -----------------------------------------------------------------------
+ // Put in a little dead-man's switch. If this function doesn't exit for a
+ // long time, let us get into the debugger.
+ // -----------------------------------------------------------------------
+
+ deadManNow = Platform::GetMilliseconds ();
+ if ((deadManNow - deadManStart) > 5000)
+ {
+ Platform::Debugger ();
+ }
+#endif
+
+ // -----------------------------------------------------------------------
+ // See if we need to halt CPU execution at this location. We could need
+ // to do this for several reasons, including hitting soft breakpoints or
+ // needing to execute tailpatches.
+ // -----------------------------------------------------------------------
+
+ if (MetaMemory::IsCPUBreak (pc_meta_oldp + (pc_p - pc_oldp)))
+ {
+ EmAssert (session);
+ session->HandleInstructionBreak ();
+ }
+
+#if HAS_PROFILING
+ emuptr pcStart;
+ pcStart = m68k_getpc ();
+
+ if (gProfilingEnabled)
+ {
+ // If detailed, log instruction here
+
+ if (gProfilingDetailed)
+ {
+ ProfileInstructionEnter (pcStart);
+ }
+
+#if HAS_PROFILING_DEBUG
+ readCyclesSaved = gReadCycles;
+ writeCyclesSaved = gWriteCycles;
+ clockCyclesSaved = gClockCycles;
+#endif
+
+ // Turn gProfilingCounted on here so the opcode fetch below is counted.
+
+ gProfilingCounted = true;
+ }
+#endif
+
+ // =======================================================================
+ // Execute the opcode.
+ // -----------------------------------------------------------------------
+ // Interestingly, defining "opcode" as an EmOpcode68K (which is a uint32)
+ // instead of a uint16 (which is all you need to hold the opcode), makes
+ // a big difference in performance (at least, on Intel). The former
+ // generates:
+ //
+ // mov edx, DWORD PTR _regs+92
+ // xor eax, eax
+ // mov ax, WORD PTR [edx]
+ // push eax
+ // call DWORD PTR [edi+eax*4]
+ //
+ // while the latter generates:
+ //
+ // mov edx, DWORD PTR _regs+92
+ // mov ax, WORD PTR [edx]
+ // and eax, 65535 ; 0000ffffH
+ // push eax
+ // call DWORD PTR [edi+eax*4]
+ //
+ // This results in an amazing 5% performance difference. What makes it
+ // even more amazing is that similar optimizations elsewhere don't seem
+ // to help. For instance, in EmCPU68K::Cycle, there's the expression:
+ //
+ // ((++gCounter) & 0x7FFF == 0)
+ //
+ // Changing gCounter to a uint16, or changing the expression to:
+ //
+ // ((uint16) ++gCounter) == 0)
+ //
+ // both result in the period doubling, but also in better code
+ // generation. However, neither gives better performance. Additionally,
+ // I tried like heck to optimize the call to IsCPUBreak above. I added
+ // a preflight check that would skip 70% of the calls to IsCPUBreak (as
+ // well as the calculation of the parameter passed to it) and re-
+ // organized the code so that branch prediction would work. However,
+ // none of that resulted in better performance.
+ // -----------------------------------------------------------------------
+ EmOpcode68K opcode;
+ // opcode = get_iword (0);
+#if HAS_PROFILING
+ if (gProfilingEnabled)
+ get_word(regs.pc + ((char*) pc_p - (char*) pc_oldp));
+#endif
+ opcode = do_get_mem_word (pc_p);
+ fCycleCount += (functable[opcode]) (opcode);
+ // =======================================================================
+
+#if HAS_PROFILING
+ if (gProfilingEnabled)
+ {
+ // Add in the extra time taken to execute the instruction.
+
+ ProfileIncrementClock (perftbl[opcode].extraCycles);
+
+ // Detail (instruction level) profiling support
+
+ if (gProfilingDetailed)
+ {
+ ProfileInstructionExit (pcStart);
+ }
+
+#if HAS_PROFILING_DEBUG
+ // Validity check on EmMemory stuff.
+
+ Boolean tryAgain = false;
+
+ if (perftbl[opcode].readCycles != 0xFF &&
+ gReadCycles - readCyclesSaved != perftbl[opcode].readCycles)
+ {
+ gReadMismatch += gReadCycles - readCyclesSaved - perftbl[opcode].readCycles;
+ }
+
+ if (perftbl[opcode].writeCycles != 0xFF &&
+ gWriteCycles - writeCyclesSaved != perftbl[opcode].writeCycles)
+ {
+ gWriteMismatch += gWriteCycles - writeCyclesSaved - perftbl[opcode].writeCycles;
+ }
+
+ if (tryAgain)
+ {
+ (functable[opcode]) (opcode);
+ }
+#endif
+ }
+#endif
+
+ // Perform periodic tasks.
+
+ CYCLE (false);
+
+StoppedLoop:
+
+ // -----------------------------------------------------------------------
+ // Handle special conditions. NB: the code reached by calling
+ // EmCPU68K::ExecuteSpecial used to be inline in this function. Moving it
+ // out (thus simplifying both EmCPU68K::Execute and EmCPU68K::ExecuteSpecial)
+ // sped up the CPU loop by 9%!
+ // -----------------------------------------------------------------------
+
+ if (spcflags)
+ {
+ if (this->ExecuteSpecial ())
+ break;
+ }
+
+#if HAS_PROFILING
+ gProfilingCounted = false;
+#endif
+ } // while (1)
+
+#undef pc_p
+#undef pc_meta_oldp
+#undef pc_oldp
+#undef spcflags
+#undef session
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::ExecuteSimple
+// ---------------------------------------------------------------------------
+
+/*
+ This is the same loop with the following removed:
+
+ * Comments
+ * Profiling code
+ * Profiling debugging code
+ * Dead mans's check on Mac
+ * Register history recording
+
+ It's essentially what we get in Release/Non-profile builds.
+*/
+
+#if 0
+
+void EmCPU68K::ExecuteSimple (void)
+{
+ if ((regs.spcflags & SPCFLAG_STOP) != 0)
+ goto StoppedLoop;
+
+ while (1)
+ {
+ if (MetaMemory::IsCPUBreak (regs.pc_meta_oldp + (regs.pc_p - regs.pc_oldp)))
+ {
+ this->HandleInstructionBreak ();
+ }
+
+ EmOpcode68K opcode;
+ opcode = get_iword (0);
+ fCycleCount += cpufunctbl[opcode] (opcode);
+
+ this->Cycle (false);
+
+StoppedLoop:
+ if (regs.spcflags)
+ {
+ if (this->ExecuteSpecial ())
+ break;
+ }
+ }
+}
+
+#endif
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::ExecuteSpecial
+// ---------------------------------------------------------------------------
+
+Bool EmCPU68K::ExecuteSpecial (void)
+{
+ // If we're making subroutine calls, then all we process are requests
+ // to break from the CPU loop. We don't want interrupts, tracing, etc.
+ // getting in the way.
+
+ EmAssert (fSession);
+ if (fSession->IsNested () != 0)
+ {
+ return this->CheckForBreak ();
+ }
+
+ // Check for Reset first. If this is set, don't do anything else.
+
+ if ((regs.spcflags & SPCFLAG_END_OF_CYCLE))
+ {
+ if (fSession->ExecuteSpecial (true))
+ return true;
+ }
+
+ // Execute UAE spcflags-handling code (from do_specialties in newcpu.c).
+
+ // If we're tracing, execute the trace vector.
+ //
+ // The check for SPCFLAG_STOP was added in Poser. It's needed
+ // if we're re-entering ExecuteStopped loop on the Mac after
+ // exiting in order to handle events.
+
+ if ((regs.spcflags & SPCFLAG_DOTRACE) && !(regs.spcflags & SPCFLAG_STOP))
+ {
+ this->ProcessException (kException_Trace);
+ }
+
+ if (regs.spcflags & SPCFLAG_STOP)
+ {
+ if (this->ExecuteStoppedLoop ())
+ {
+ regs.spcflags &= ~SPCFLAG_BRK;
+ return true;
+ }
+ }
+
+ // Do trace-mode stuff (do_trace from newcpu.c does more,
+ // but it's only needed for CPU_LEVEL > 0)
+
+ if (regs.spcflags & SPCFLAG_TRACE)
+ {
+ if (regs.t1)
+ {
+ fLastTraceAddress = m68k_getpc ();
+ regs.spcflags &= ~SPCFLAG_TRACE;
+ regs.spcflags |= SPCFLAG_DOTRACE;
+ }
+ }
+
+ if ((regs.spcflags & SPCFLAG_DOINT) && !gKernelStackOverflowed)
+ {
+ int32 interruptLevel = EmHAL::GetInterruptLevel ();
+ regs.spcflags &= ~SPCFLAG_DOINT; // was ~(SPCFLAG_INT | SPCFLAG_DOINT) in Greg and Craig, but the latest UAE has this
+ if ((interruptLevel != -1) && (interruptLevel > regs.intmask))
+ {
+ this->ProcessInterrupt (interruptLevel);
+ regs.stopped = 0;
+ }
+ }
+
+ if (regs.spcflags & SPCFLAG_INT)
+ {
+ regs.spcflags &= ~SPCFLAG_INT;
+ regs.spcflags |= SPCFLAG_DOINT;
+ }
+
+ // Check for Poser end-of-cycle operations. This is inserted
+ // before the standard UAE check of the SPCFLAG_BRK flag.
+
+ if ((regs.spcflags & SPCFLAG_END_OF_CYCLE))
+ {
+ regs.spcflags &= ~SPCFLAG_END_OF_CYCLE;
+
+ if (fSession->ExecuteSpecial (false))
+ return true;
+ }
+
+ return this->CheckForBreak ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::ExecuteStoppedLoop
+// ---------------------------------------------------------------------------
+
+Bool EmCPU68K::ExecuteStoppedLoop (void)
+{
+ register EmSession* session = fSession;
+
+ EmAssert (session);
+ EmAssert (!session->IsNested ());
+ EmAssert (regs.intmask < 7);
+
+ // If we're running Gremlins and the device goes to sleep (as opposed
+ // to just dozing), pretend the user clicked on the Power button).
+
+ if (EmHAL::GetAsleep () && !session->HasButtonEvent ())
+ {
+ // Turns Hordes off for a moment, as pen events are rejected
+ // when it's running!
+
+ Bool hordesWasOn = false;
+ Bool playbackWasOn = false;
+ Bool minimizationWasOn = false;
+
+ if (Hordes::IsOn ())
+ {
+ Hordes::TurnOn (false);
+ hordesWasOn = true;
+ }
+
+ if (EmEventPlayback::ReplayingEvents ())
+ {
+ EmEventPlayback::ReplayEvents (false);
+ playbackWasOn = true;
+ }
+
+ if (EmMinimize::IsOn ())
+ {
+ EmMinimize::TurnOn (false);
+ minimizationWasOn = true;
+ }
+
+ /*
+ When posting the button down event for the Power button, include
+ a flag that causes the event to be posted to the hardware NOW.
+ Normally, EmSession employs a throttling mechanism that prevents
+ button events from showing up in the hardware too quickly. This
+ throttling allows the time for the OS to wakeup and respond to
+ the hardware interrupt.
+
+ However, the throttling also leads to an incredibly subtle bug
+ if it's used unconditionally. Consider the following sequence
+ of events:
+
+ * Gremlins creates a vchrAutoOff event
+ * The system responds to it and goes to sleep.
+ * Poser notices the device is asleep while running a Gremlin,
+ and posts Power button down/up events.
+ * It's been more than 100msec since the last button event, so
+ the button event is given to the hardware immediately.
+ * The system wakes up, notices that the Power key is down, and
+ sets the flag that says it should stay awake.
+ * Gremlins starts posting events again.
+ * Within 100msecs, Gremlins generates another vchrAutoOff event.
+ * The devices goes to sleep again. Somewhere in the process of
+ dealing with the vchrAutoOff event, the Power-button up event
+ is popped off and handled.
+ * Poser posts Power button down/up events again.
+ * Because it's been within 100msecs of the last time a button
+ event was popped off the queue, these events are held in the
+ queue for a while.
+ * An RTC interrupt occurs.
+ * The device wakes up, notices that the Power is not down, and
+ does NOT set the bit that says to stay awake.
+ * The devices goes back to sleep after processing the RTC
+ interrupt. Note that there are still two Power-button events
+ in EmSession's button queue.
+ * Poser notices that the device is off, and posts *another*
+ pair of Power-button down/up events.
+ * At this point, Poser is in a weird state, where there are too
+ many events in the queue. In fact, the situation will get
+ worse, with more and more events piling up and the system
+ deals with turning itself on and off in response to the pending
+ button events. The result is that the user sees the device
+ flashing on and off.
+
+ In order to avoid this problem, have the Power button posted to
+ the hardware registers immediately, before an interrupt can get
+ in the way.
+
+ Note that there is another potential bug here. We avoid it for
+ now, but be careful not to introduce it later. By passing "true"
+ to PostButtonEvent, we can make the button event available to
+ the hardware immediately. However, the event is not forced onto
+ the hardware. Instead, the hardware polls for any pending button
+ events at some reasonable rate. That is, even if EmSession is
+ prepared to say that there is a pending button event, EmCPU may
+ not pick up on it until some time later based on its checking
+ frequency.
+
+ We avoid this problem for now because the hardware checks for
+ button events in EmHAL::CycleSlowly, which is called
+ unconditionally when the processor is moved out of the "stopped"
+ state.
+ */
+
+ if (hordesWasOn || playbackWasOn || minimizationWasOn)
+ {
+ EmButtonEvent event (kElement_PowerButton, true);
+ session->PostButtonEvent (event, true);
+
+ event.fButtonIsDown = false;
+ session->PostButtonEvent (event);
+ }
+
+ if (hordesWasOn)
+ {
+ Hordes::TurnOn (true);
+ }
+
+ if (playbackWasOn)
+ {
+ EmEventPlayback::ReplayEvents (true);
+ }
+
+ if (minimizationWasOn)
+ {
+ EmMinimize::TurnOn (true);
+ }
+ }
+
+ int counter = 0;
+
+ // While the CPU is stopped (because a STOP instruction was
+ // executed) do some idle tasks.
+
+#if HAS_DEAD_MANS_SWITCH
+ // -----------------------------------------------------------------------
+ // Put in a little dead-man's switch. If this function doesn't
+ // exit for a long time, let us get into the debugger.
+ // -----------------------------------------------------------------------
+
+ uint32 deadManStart = Platform::GetMilliseconds ();
+#endif
+
+ do {
+#if HAS_DEAD_MANS_SWITCH
+ // -----------------------------------------------------------------------
+ // Put in a little dead-man's switch. If this function doesn't
+ // exit for a long time, let us get into the debugger.
+ // -----------------------------------------------------------------------
+
+ uint32 deadManNow = Platform::GetMilliseconds ();
+ if ((deadManNow - deadManStart) > 5000)
+ {
+ Platform::Debugger ();
+ }
+#endif
+
+ // -----------------------------------------------------------------------
+ // Slow down processing so that the timer used
+ // to increment the tickcount doesn't run too quickly.
+ // -----------------------------------------------------------------------
+
+#if __profile__
+ short oldStatus = ProfilerGetStatus ();
+ ProfilerSetStatus (false);
+#endif
+
+ Platform::Delay ();
+
+#if __profile__
+ ProfilerSetStatus (oldStatus);
+#endif
+
+ // Perform periodic tasks.
+
+ CYCLE (true);
+
+ // Process an interrupt (see if it's time to wake up).
+
+ if (regs.spcflags & (SPCFLAG_INT | SPCFLAG_DOINT))
+ {
+ int32 interruptLevel = EmHAL::GetInterruptLevel ();
+
+ regs.spcflags &= ~(SPCFLAG_INT | SPCFLAG_DOINT);
+
+ if ((interruptLevel != -1) && (interruptLevel > regs.intmask))
+ {
+ this->ProcessInterrupt (interruptLevel);
+ regs.stopped = 0;
+ regs.spcflags &= ~SPCFLAG_STOP;
+ }
+ }
+
+ if (this->CheckForBreak ())
+ {
+ return true;
+ }
+ } while (regs.spcflags & SPCFLAG_STOP);
+
+ return false;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::CycleSlowly
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::CycleSlowly (Bool sleeping)
+{
+ EmHAL::CycleSlowly (sleeping);
+
+ // Do some platform-specific stuff.
+
+ Platform::CycleSlowly ();
+
+#if HAS_OMNI_THREAD
+ // Check to see if some external thread has asked us to quit.
+
+ EmAssert (fSession);
+ omni_mutex_lock lock (fSession->fSharedLock);
+
+ if (fSession->fSuspendState.fAllCounters)
+ {
+ this->CheckAfterCycle ();
+ }
+#endif
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::CheckAfterCycle
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::CheckAfterCycle (void)
+{
+ regs.spcflags |= SPCFLAG_END_OF_CYCLE;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::GetPC
+// ---------------------------------------------------------------------------
+
+emuptr EmCPU68K::GetPC (void)
+{
+// return this->GetRegister (e68KRegID_PC);
+ return m68k_getpc ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::GetSP
+// ---------------------------------------------------------------------------
+
+emuptr EmCPU68K::GetSP (void)
+{
+// return this->GetRegister (e68KRegID_SSP);
+ return m68k_areg (regs, 7);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::GetRegister
+// ---------------------------------------------------------------------------
+
+uint32 EmCPU68K::GetRegister (int index)
+{
+ uint32 result = 0;
+
+ switch (index)
+ {
+ case e68KRegID_D0: result = m68k_dreg (regs, 0); break;
+ case e68KRegID_D1: result = m68k_dreg (regs, 1); break;
+ case e68KRegID_D2: result = m68k_dreg (regs, 2); break;
+ case e68KRegID_D3: result = m68k_dreg (regs, 3); break;
+ case e68KRegID_D4: result = m68k_dreg (regs, 4); break;
+ case e68KRegID_D5: result = m68k_dreg (regs, 5); break;
+ case e68KRegID_D6: result = m68k_dreg (regs, 6); break;
+ case e68KRegID_D7: result = m68k_dreg (regs, 7); break;
+ case e68KRegID_A0: result = m68k_areg (regs, 0); break;
+ case e68KRegID_A1: result = m68k_areg (regs, 1); break;
+ case e68KRegID_A2: result = m68k_areg (regs, 2); break;
+ case e68KRegID_A3: result = m68k_areg (regs, 3); break;
+ case e68KRegID_A4: result = m68k_areg (regs, 4); break;
+ case e68KRegID_A5: result = m68k_areg (regs, 5); break;
+ case e68KRegID_A6: result = m68k_areg (regs, 6); break;
+ case e68KRegID_USP: result = m68k_areg (regs, 7); break;
+ case e68KRegID_SSP: result = m68k_areg (regs, 7); break;
+ case e68KRegID_PC: result = m68k_getpc (); break;
+ case e68KRegID_SR:
+ this->UpdateSRFromRegisters ();
+ result = regs.sr;
+ break;
+
+ default:
+ EmAssert (false);
+ }
+
+ return result;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::SetPC
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::SetPC (emuptr newPC)
+{
+ this->SetRegister (e68KRegID_PC, newPC);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::SetSP
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::SetSP (emuptr newPC)
+{
+ this->SetRegister (e68KRegID_SSP, newPC);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::SetRegister
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::SetRegister (int index, uint32 val)
+{
+ switch (index)
+ {
+ case e68KRegID_D0: m68k_dreg (regs, 0) = val; break;
+ case e68KRegID_D1: m68k_dreg (regs, 1) = val; break;
+ case e68KRegID_D2: m68k_dreg (regs, 2) = val; break;
+ case e68KRegID_D3: m68k_dreg (regs, 3) = val; break;
+ case e68KRegID_D4: m68k_dreg (regs, 4) = val; break;
+ case e68KRegID_D5: m68k_dreg (regs, 5) = val; break;
+ case e68KRegID_D6: m68k_dreg (regs, 6) = val; break;
+ case e68KRegID_D7: m68k_dreg (regs, 7) = val; break;
+ case e68KRegID_A0: m68k_areg (regs, 0) = val; break;
+ case e68KRegID_A1: m68k_areg (regs, 1) = val; break;
+ case e68KRegID_A2: m68k_areg (regs, 2) = val; break;
+ case e68KRegID_A3: m68k_areg (regs, 3) = val; break;
+ case e68KRegID_A4: m68k_areg (regs, 4) = val; break;
+ case e68KRegID_A5: m68k_areg (regs, 5) = val; break;
+ case e68KRegID_A6: m68k_areg (regs, 6) = val; break;
+ case e68KRegID_USP: m68k_areg (regs, 7) = val; break;
+ case e68KRegID_SSP: m68k_areg (regs, 7) = val; break;
+ case e68KRegID_PC: m68k_setpc (val); break;
+ case e68KRegID_SR:
+ regs.sr = val;
+ this->UpdateRegistersFromSR ();
+ break;
+
+ default:
+ EmAssert (false);
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::Stopped
+// ---------------------------------------------------------------------------
+// Return whether or not the CPU itself is halted. This is seperate from
+// whether or not the session (that is, the thread emulating the CPU) is
+// halted.
+
+Bool EmCPU68K::Stopped (void)
+{
+ return regs.stopped;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::CheckForBreak
+// ---------------------------------------------------------------------------
+// Check to see if the conditions tell us to break from the CPU Execute loop.
+
+Bool EmCPU68K::CheckForBreak (void)
+{
+ if ((regs.spcflags & SPCFLAG_BRK) != 0)
+ {
+ regs.spcflags &= ~SPCFLAG_BRK;
+ return true;
+ }
+
+ EmAssert (fSession);
+ return fSession->CheckForBreak ();
+}
+
+
+#pragma mark -
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::ProcessInterrupt
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::ProcessInterrupt (int32 interrupt)
+{
+ this->ProcessException ((ExceptionNumber) (EmHAL::GetInterruptBase () + interrupt));
+
+ regs.intmask = interrupt;
+ regs.spcflags |= SPCFLAG_INT; // Check for higher-level interrupts
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::ProcessException
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::ProcessException (ExceptionNumber exception)
+{
+ // Make sure the Status Register is up-to-date.
+
+ this->UpdateSRFromRegisters ();
+
+#if EXCEPTION_HISTORY
+ // Save the exception for the post-mortem
+
+ fExceptionHistoryIndex++;
+
+ long index = fExceptionHistoryIndex & (kExceptionHistorySize - 1);
+ fExceptionHistory[index].name = kExceptionNames[exception];
+
+ fExceptionHistory[index].pc = m68k_getpc ();
+ fExceptionHistory[index].sp = m68k_areg (regs, 7);
+#endif
+
+#if HAS_PROFILING
+ // Don't count cycles spent in exception handlers against functions.
+
+ // Get and remember the current PC here. This is important to make the
+ // profiling routines come out right. If Poser decides to completely
+ // handle the exception and profiling is on, then the handler will return
+ // true (setting "handled" to true), and adjust the program counter.
+ // Since "handled" is true, Poser calls ProfileInterruptExit, this time
+ // with the current PC. ProfileInterruptExit sees the adjusted PC, and
+ // determines that the funky TRAP $F thing is going on, resulting in it
+ // pushing a new function-call record on its stack. However, since Poser
+ // has completely handled the interrupt, we don't want that to happen.
+ // All we want is to remove the entry recorded by ProfileInterruptEnter.
+ // By saving the current PC value here and passing it to ProfileInterruptExit
+ // later, we achieve that affect. ProfileInterruptExit will record an
+ // "interrupt mismatch", but I can live with that...
+
+ emuptr curpc = m68k_getpc ();
+
+ if (gProfilingEnabled)
+ {
+ ProfileInterruptEnter (exception, curpc);
+ }
+#endif
+
+ // Let any custom exception handler have a go at it. If it returns
+ // true, it handled it completely, and we don't have anything else to do.
+ //
+ // By "handled it completely", we could mean one of two things. (1)
+ // that Poser completely replaced the system function and the ROM
+ // function was skipped, or (2) Poser dealt with the trap dispatch
+ // process, setting the PC to the function entry point.
+ //
+ // If profiling is on, (2) never occurs because we want to profile the
+ // trap dispatcher itself. Therefore, only (1) is possible.
+
+ Bool handled = false;
+ Hook68KExceptionList& fns = fExceptionHandlers[exception];
+ Hook68KExceptionList::iterator iter = fns.begin ();
+ while (iter != fns.end ())
+ {
+ if ((*iter) (exception))
+ {
+ handled = true;
+ }
+
+ ++iter;
+ }
+
+ if (handled)
+ {
+#if HAS_PROFILING
+ if (gProfilingEnabled)
+ {
+ ProfileInterruptExit (curpc);
+ }
+#endif
+ return;
+ }
+
+ // The following is vaguely modelled after Exception() from newcpu.c
+ // (The call to MakeSR appears at the start of this method).
+
+ // If not in supervisor mode, set the usp, restore A7 from the isp,
+ // and transfer to supervisor mode.
+
+ if (!regs.s)
+ {
+ regs.usp = m68k_areg (regs, 7);
+ m68k_areg (regs, 7) = regs.isp;
+ regs.s = 1;
+ }
+
+ // Set up the stack frame.
+ // !!! If we're handling a trace exception, I think that fLastTraceAddress
+ // comes into play here instead of m68k_getpc.
+ // !!! Manage this with EmPalmStructs...
+
+ if (exception == kException_BusErr || exception == kException_AddressErr)
+ {
+ COMPILE_TIME_ASSERT (sizeof (ExceptionStackFrame2) == 14);
+ m68k_areg (regs, 7) -= sizeof (ExceptionStackFrame2);
+ CHECK_STACK_POINTER_DECREMENT ();
+
+ emuptr frame = m68k_areg (regs, 7);
+
+ // Eh...Palm OS doesn't use these 3 anyway...
+ EmMemPut16 (frame + offsetof (ExceptionStackFrame2, functionCode), 0);
+ EmMemPut32 (frame + offsetof (ExceptionStackFrame2, accessAddress), 0);
+ EmMemPut16 (frame + offsetof (ExceptionStackFrame2, instructionRegister), 0);
+
+ EmMemPut16 (frame + offsetof (ExceptionStackFrame2, statusRegister), regs.sr);
+ EmMemPut32 (frame + offsetof (ExceptionStackFrame2, programCounter), m68k_getpc ());
+ }
+ else
+ {
+ COMPILE_TIME_ASSERT (sizeof (ExceptionStackFrame1) == 6);
+ m68k_areg (regs, 7) -= sizeof (ExceptionStackFrame1);
+ CHECK_STACK_POINTER_DECREMENT ();
+
+ emuptr frame = m68k_areg (regs, 7);
+
+ EmMemPut16 (frame + offsetof (ExceptionStackFrame1, statusRegister), regs.sr);
+ EmMemPut32 (frame + offsetof (ExceptionStackFrame1, programCounter), m68k_getpc ());
+ }
+
+ emuptr newpc;
+ {
+ CEnableFullAccess munge; // Remove blocks on memory access.
+
+ // Get the exception handler address.
+ newpc = EmMemGet32 (regs.vbr + 4 * exception);
+ }
+
+ // Check the exception handler address and jam it into the PC.
+
+ this->CheckNewPC (newpc);
+ m68k_setpc (newpc);
+
+ // Turn tracing off.
+
+ regs.t1 = regs.t0 = regs.m = 0;
+ regs.spcflags &= ~(SPCFLAG_TRACE | SPCFLAG_DOTRACE);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::ProcessIllegalInstruction
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::ProcessIllegalInstruction (EmOpcode68K opcode)
+{
+ // This function is loosely based on op_illg in newcpu.c
+
+ // Process an FTrap.
+
+ if ((opcode & 0xF000) == 0xF000)
+ {
+ this->ProcessException (kException_FTrap);
+ }
+
+ // Process an ATrap.
+
+ else if ((opcode & 0xF000) == 0xA000)
+ {
+ this->ProcessException (kException_ATrap);
+ }
+
+ // Process all other opcodes.
+
+ else
+ {
+ this->ProcessException (kException_IllegalInstr);
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::ProcessJSR
+// ---------------------------------------------------------------------------
+
+int EmCPU68K::ProcessJSR (emuptr oldPC, emuptr dest)
+{
+ int handled = false;
+
+ Hook68KJSRList::iterator iter = fHookJSR.begin ();
+ while (iter != fHookJSR.end ())
+ {
+ if ((*iter) (oldPC, dest))
+ {
+ handled = true;
+ }
+
+ ++iter;
+ }
+
+ return handled;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::ProcessJSR_Ind
+// ---------------------------------------------------------------------------
+
+int EmCPU68K::ProcessJSR_Ind (emuptr oldPC, emuptr dest)
+{
+ int handled = false;
+
+ Hook68KJSR_IndList::iterator iter = fHookJSR_Ind.begin ();
+ while (iter != fHookJSR_Ind.end ())
+ {
+ if ((*iter) (oldPC, dest))
+ {
+ handled = true;
+ }
+
+ ++iter;
+ }
+
+ return handled;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::ProcessLINK
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::ProcessLINK (int linkSize)
+{
+ Hook68KLINKList::iterator iter = fHookLINK.begin ();
+ while (iter != fHookLINK.end ())
+ {
+ (*iter) (linkSize);
+
+ ++iter;
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::ProcessRTE
+// ---------------------------------------------------------------------------
+
+int EmCPU68K::ProcessRTE (emuptr dest)
+{
+ int handled = false;
+
+ Hook68KRTEList::iterator iter = fHookRTE.begin ();
+ while (iter != fHookRTE.end ())
+ {
+ if ((*iter) (dest))
+ {
+ handled = true;
+ }
+
+ ++iter;
+ }
+
+ return handled;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::ProcessRTS
+// ---------------------------------------------------------------------------
+
+int EmCPU68K::ProcessRTS (emuptr dest)
+{
+ int handled = false;
+
+ Hook68KRTSList::iterator iter = fHookRTS.begin ();
+ while (iter != fHookRTS.end ())
+ {
+ if ((*iter) (dest))
+ {
+ handled = true;
+ }
+
+ ++iter;
+ }
+
+ return handled;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::CheckNewPC
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::CheckNewPC (emuptr dest)
+{
+ Hook68KNewPCList::iterator iter = fHookNewPC.begin ();
+ while (iter != fHookNewPC.end ())
+ {
+ (*iter) (dest);
+
+ ++iter;
+ }
+
+ EmMemory::CheckNewPC (dest);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::CheckNewSP
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::CheckNewSP (EmStackChangeType type)
+{
+ Hook68KNewSPList::iterator iter = fHookNewSP.begin ();
+ while (iter != fHookNewSP.end ())
+ {
+ (*iter) (type);
+
+ ++iter;
+ }
+}
+
+
+#pragma mark -
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::InstallHookException
+// ¥ EmCPU68K::InstallHookJSR
+// ¥ EmCPU68K::InstallHookJSR_Ind
+// ¥ EmCPU68K::InstallHookLINK
+// ¥ EmCPU68K::InstallHookRTE
+// ¥ EmCPU68K::InstallHookRTS
+// ¥ EmCPU68K::InstallHookNewPC
+// ¥ EmCPU68K::InstallHookNewSP
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::InstallHookException (ExceptionNumber exceptionNumber,
+ Hook68KException fn)
+{
+ fExceptionHandlers[exceptionNumber].push_back (fn);
+}
+
+
+void EmCPU68K::InstallHookJSR (Hook68KJSR fn)
+{
+ fHookJSR.push_back (fn);
+}
+
+
+void EmCPU68K::InstallHookJSR_Ind (Hook68KJSR_Ind fn)
+{
+ fHookJSR_Ind.push_back (fn);
+}
+
+
+void EmCPU68K::InstallHookLINK (Hook68KLINK fn)
+{
+ fHookLINK.push_back (fn);
+}
+
+
+void EmCPU68K::InstallHookRTE (Hook68KRTE fn)
+{
+ fHookRTE.push_back (fn);
+}
+
+
+void EmCPU68K::InstallHookRTS (Hook68KRTS fn)
+{
+ fHookRTS.push_back (fn);
+}
+
+
+void EmCPU68K::InstallHookNewPC (Hook68KNewPC fn)
+{
+ fHookNewPC.push_back (fn);
+}
+
+
+void EmCPU68K::InstallHookNewSP (Hook68KNewSP fn)
+{
+ fHookNewSP.push_back (fn);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::RemoveHookException
+// ¥ EmCPU68K::RemoveHookJSR
+// ¥ EmCPU68K::RemoveHookJSR_Ind
+// ¥ EmCPU68K::RemoveHookLINK
+// ¥ EmCPU68K::RemoveHookRTE
+// ¥ EmCPU68K::RemoveHookRTS
+// ¥ EmCPU68K::RemoveHookNewPC
+// ¥ EmCPU68K::RemoveHookNewSP
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::RemoveHookException (ExceptionNumber exceptionNumber,
+ Hook68KException fn)
+{
+ Hook68KExceptionList::iterator iter = find (
+ fExceptionHandlers[exceptionNumber].begin (),
+ fExceptionHandlers[exceptionNumber].end (),
+ fn);
+
+ if (iter != fExceptionHandlers[exceptionNumber].end ())
+ {
+ fExceptionHandlers[exceptionNumber].erase (iter);
+ }
+}
+
+
+void EmCPU68K::RemoveHookJSR (Hook68KJSR fn)
+{
+ Hook68KJSRList::iterator iter = find (fHookJSR.begin (), fHookJSR.end (), fn);
+
+ if (iter != fHookJSR.end ())
+ {
+ fHookJSR.erase (iter);
+ }
+}
+
+
+void EmCPU68K::RemoveHookJSR_Ind (Hook68KJSR_Ind fn)
+{
+ Hook68KJSR_IndList::iterator iter = find (fHookJSR_Ind.begin (), fHookJSR_Ind.end (), fn);
+
+ if (iter != fHookJSR_Ind.end ())
+ {
+ fHookJSR_Ind.erase (iter);
+ }
+}
+
+
+void EmCPU68K::RemoveHookLINK (Hook68KLINK fn)
+{
+ Hook68KLINKList::iterator iter = find (fHookLINK.begin (), fHookLINK.end (), fn);
+
+ if (iter != fHookLINK.end ())
+ {
+ fHookLINK.erase (iter);
+ }
+}
+
+
+void EmCPU68K::RemoveHookRTE (Hook68KRTE fn)
+{
+ Hook68KRTEList::iterator iter = find (fHookRTE.begin (), fHookRTE.end (), fn);
+
+ if (iter != fHookRTE.end ())
+ {
+ fHookRTE.erase (iter);
+ }
+}
+
+
+void EmCPU68K::RemoveHookRTS (Hook68KRTS fn)
+{
+ Hook68KRTSList::iterator iter = find (fHookRTS.begin (), fHookRTS.end (), fn);
+
+ if (iter != fHookRTS.end ())
+ {
+ fHookRTS.erase (iter);
+ }
+}
+
+
+void EmCPU68K::RemoveHookNewPC (Hook68KNewPC fn)
+{
+ Hook68KNewPCList::iterator iter = find (fHookNewPC.begin (), fHookNewPC.end (), fn);
+
+ if (iter != fHookNewPC.end ())
+ {
+ fHookNewPC.erase (iter);
+ }
+}
+
+
+void EmCPU68K::RemoveHookNewSP (Hook68KNewSP fn)
+{
+ Hook68KNewSPList::iterator iter = find (fHookNewSP.begin (), fHookNewSP.end (), fn);
+
+ if (iter != fHookNewSP.end ())
+ {
+ fHookNewSP.erase (iter);
+ }
+}
+
+
+#pragma mark -
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::GetRegisters
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::GetRegisters (regstruct& registers)
+{
+ this->UpdateSRFromRegisters ();
+
+ registers = regs;
+ registers.pc = m68k_getpc ();
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::SetRegisters
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::SetRegisters (regstruct& registers)
+{
+ regs = registers;
+ this->UpdateRegistersFromSR ();
+
+ m68k_setpc (registers.pc);
+
+ this->CheckNewSP (kStackPointerChanged);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::UpdateSRFromRegisters
+// ---------------------------------------------------------------------------
+// Create a 16-bit status register value from the broken out fields. In
+// general, we keep the various fields separate for speed of access. However,
+// there are times when we need the packed 16-bit field.
+//
+// This function is called any time the 16-bit representation of the SR is
+// needed:
+//
+// on EORSR
+// on ORSR
+// on ANDSR
+// on MVSR2
+// on MV2SR
+// on RTR
+// when processing exceptions (EmCPU68K::ProcessException)
+//
+// SystemPacket::GetRegs
+// EmCPU68K::GetRegisters
+
+void EmCPU68K::UpdateSRFromRegisters (void)
+{
+ // (taken from MakeSR in newcpu.c)
+
+ regs.sr = ((regs.t1 << 15) | (regs.t0 << 14)
+ | (regs.s << 13) | (regs.m << 12) | (regs.intmask << 8)
+ | (GET_XFLG << 4) | (GET_NFLG << 3) | (GET_ZFLG << 2) | (GET_VFLG << 1)
+ | GET_CFLG);
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::UpdateRegistersFromSR
+// ---------------------------------------------------------------------------
+// Break out all of the fields from the 16-bit status register into their own
+// separate variables.
+//
+// This function is called any time the SR has been update and needs to be
+// re-expanded:
+//
+// on EORSR
+// on ORSR
+// on ANDSR
+// on MV2SR
+// on STOP
+// on RTE
+// on RTR
+//
+// SystemPacket::SetRegs
+// EmCPU68K::SetRegisters
+
+void EmCPU68K::UpdateRegistersFromSR (void)
+{
+ // (taken from MakeFromSR in newcpu.c)
+
+// int oldm = regs.m;
+ int olds = regs.s;
+
+ regs.t1 = (regs.sr >> 15) & 1;
+ regs.t0 = (regs.sr >> 14) & 1;
+ regs.s = (regs.sr >> 13) & 1;
+ regs.m = (regs.sr >> 12) & 1;
+ regs.intmask = (regs.sr >> 8) & 7;
+
+ SET_XFLG ((regs.sr >> 4) & 1);
+ SET_NFLG ((regs.sr >> 3) & 1);
+ SET_ZFLG ((regs.sr >> 2) & 1);
+ SET_VFLG ((regs.sr >> 1) & 1);
+ SET_CFLG (regs.sr & 1);
+
+ if (olds != regs.s)
+ {
+ if (olds)
+ {
+ regs.isp = m68k_areg(regs, 7);
+ m68k_areg(regs, 7) = regs.usp;
+ }
+ else
+ {
+ regs.usp = m68k_areg(regs, 7);
+ m68k_areg(regs, 7) = regs.isp;
+ }
+ }
+
+ regs.spcflags |= SPCFLAG_INT;
+
+ if (regs.t1 || regs.t0)
+ {
+ regs.spcflags |= SPCFLAG_TRACE;
+ }
+ else
+ {
+ regs.spcflags &= ~(SPCFLAG_TRACE | SPCFLAG_DOTRACE);
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::GetCycleCount
+// ---------------------------------------------------------------------------
+
+uint32 EmCPU68K::GetCycleCount (void)
+{
+ return fCycleCount;
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::BusError
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::BusError (emuptr address, long size, Bool forRead)
+{
+ gExceptionAddress = address;
+ gExceptionSize = size;
+ gExceptionForRead = forRead;
+
+ this->ProcessException (kException_BusErr);
+
+ EmAssert (false); // Should never get this far.
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::AddressError
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::AddressError (emuptr address, long size, Bool forRead)
+{
+ gExceptionAddress = address;
+ gExceptionSize = size;
+ gExceptionForRead = forRead;
+
+ this->ProcessException (kException_AddressErr);
+
+ EmAssert (false); // Should never get this far.
+}
+
+
+#pragma mark -
+
+// ---------------------------------------------------------------------------
+// ¥ EmCPU68K::InitializeUAETables
+// ---------------------------------------------------------------------------
+
+void EmCPU68K::InitializeUAETables (void)
+{
+ static int initialized;
+
+ // All of the stuff in this function needs to be done only once;
+ // it doesn't need to be executed every time we create a new CPU.
+
+ if (initialized)
+ return;
+
+ initialized = true;
+
+ // Initialize some CPU-related tables
+ // (This initialization code is taken from init_m68k in newcpu.c)
+
+ int i, j;
+
+ for (i = 0 ; i < 256 ; i++)
+ {
+ for (j = 0 ; j < 8 ; j++)
+ {
+ if (i & (1 << j))
+ {
+ break;
+ }
+ }
+
+ movem_index1[i] = j;
+ movem_index2[i] = 7-j;
+ movem_next[i] = i & (~(1 << j));
+ }
+
+ read_table68k ();
+ do_merges ();
+
+ // The rest of this code is based on build_cpufunctbl in newcpu.c.
+
+ unsigned long opcode;
+ struct cputbl* tbl = op_smalltbl_3;
+
+ for (opcode = 0; opcode < 65536; opcode++)
+ {
+ cpufunctbl[opcode] = op_illg;
+ }
+
+ for (i = 0; tbl[i].handler != NULL; i++)
+ {
+ if (!tbl[i].specific)
+ {
+ cpufunctbl[tbl[i].opcode] = tbl[i].handler;
+#if HAS_PROFILING
+ perftbl[tbl[i].opcode] = tbl[i].perf;
+#endif
+ }
+ }
+
+ for (opcode = 0; opcode < 65536; opcode++)
+ {
+ cpuop_func* f;
+
+ if (table68k[opcode].mnemo == i_ILLG || table68k[opcode].clev > 0)
+ {
+ continue;
+ }
+
+ if (table68k[opcode].handler != -1)
+ {
+ f = cpufunctbl[table68k[opcode].handler];
+ if (f == op_illg)
+ {
+ abort ();
+ }
+
+ cpufunctbl[opcode] = f;
+#if HAS_PROFILING
+ perftbl[opcode] = perftbl[table68k[opcode].handler];
+#endif
+
+ }
+ }
+
+ for (i = 0; tbl[i].handler != NULL; i++)
+ {
+ if (tbl[i].specific)
+ {
+ cpufunctbl[tbl[i].opcode] = tbl[i].handler;
+#if HAS_PROFILING
+ perftbl[tbl[i].opcode] = tbl[i].perf;
+#endif
+ }
+ }
+
+ // (hey readcpu doesn't free this guy!)
+
+ Platform::DisposeMemory (table68k);
+}