aboutsummaryrefslogtreecommitdiff
path: root/SrcShared/EmSession.h
diff options
context:
space:
mode:
Diffstat (limited to 'SrcShared/EmSession.h')
-rw-r--r--SrcShared/EmSession.h728
1 files changed, 728 insertions, 0 deletions
diff --git a/SrcShared/EmSession.h b/SrcShared/EmSession.h
new file mode 100644
index 0000000..a514ad6
--- /dev/null
+++ b/SrcShared/EmSession.h
@@ -0,0 +1,728 @@
+/* -*- 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.
+\* ===================================================================== */
+
+#ifndef EmSession_h
+#define EmSession_h
+
+#include "EmDevice.h" // EmDevice
+#include "EmDlg.h" // EmDlgItemID, EmCommonDialogFlags
+#include "EmThreadSafeQueue.h" // EmThreadSafeQueue
+#include "Skins.h" // SkinElementType
+
+#if HAS_OMNI_THREAD
+#include "omnithread.h" // omni_thread, omni_mutex, omni_condition
+#endif
+
+/*
+** EmSession is the class used to manage an emulation session. Its
+** responsibilities are:
+**
+** * Create new session
+** * Save session to disk
+** * Load old session from disk or resources
+** * Manage the "CPU loop"
+** * Synchronize with running session
+** * Destroy session
+**
+** That's the main abstraction. However, as you can tell from the
+** interface, a number of other duties have crept in, including:
+**
+** * UI interaction
+** * Gremlins coordination
+** * Instruction and data breakpoints
+*/
+
+
+// ---------------------------------------------------------------------------
+#pragma mark EmSuspendCounters
+// ---------------------------------------------------------------------------
+
+/*
+** The CPU loop can be suspended for any number of reasons by any number of
+** sources. EmSuspendCounters records which clients have requested that
+** the CPU loop be suspended and for what reason. To support nesting, it
+** keeps track of those statistics as counters instead of booleans. If
+** it's OK by any client for the CPU loop to run, its counter is zero.
+** Otherwise, it gets incremented to non-zero. When the CPU loop checks to
+** see if it's OK to run, it looks at all of these counters en masse via
+** the union nature of EmSuspendState.
+*/
+
+struct EmSuspendCounters
+{
+ // Incremented by calls to EmSession::SuspendThread. Decremented on
+ // calls to EmSession::ResumeThread. For executing commands, getting
+ // LCD data, etc.
+
+ int fSuspendByUIThread : 4;
+
+ // Incremented when an exception or error occurs. Decremented (or
+ // cleared) when the debugger sends a Continue packet.
+
+ int fSuspendByDebugger : 4;
+
+ // Incremented on calls to HostSessionSuspend, or an implicit
+ // scripting suspend occurred. Decremented on HostSessionResume.
+
+ int fSuspendByExternal : 4;
+
+ // Set when it's time for the CPU thread to exit after running for a
+ // little while. Used on the Mac, and on other systems when calling
+ // a Palm OS function that needs to occasionally return control (as
+ // when installing a file with the Exchange Manager).
+
+ int fSuspendByTimeout : 1;
+
+ // Set when we're leaving because we called Execute in order to spin
+ // the state up to a system call, and that state is reached.
+
+ int fSuspendBySysCall : 1;
+
+ // Set when we're leaving because we called Execute in order to call
+ // a subroutine, and that subroutine returned.
+
+ int fSuspendBySubroutineReturn : 1;
+};
+
+
+// ---------------------------------------------------------------------------
+#pragma mark EmSuspendState
+// ---------------------------------------------------------------------------
+
+/*
+** EmSuspendState is a union of EmSuspendCounters and a 32-bit integer. Its
+** purpose is to allow the testing of *all* the suspend flags at once. If
+** any flag is set, then emulation is suspended.
+*/
+
+union EmSuspendState
+{
+ EmSuspendCounters fCounters;
+ uint32 fAllCounters;
+};
+
+
+// ---------------------------------------------------------------------------
+#pragma mark EmSessionState
+// ---------------------------------------------------------------------------
+
+/*
+** The current state of the "CPU loop". If the stateis kSuspended, the
+** reason for that suspension can usually be found in EmSuspendState.
+*/
+
+enum EmSessionState
+{
+ kRunning, // The CPU thread is running.
+ kStopped, // The CPU thread is not started or has finished.
+ kSuspended, // The CPU thread is suspended at the end of a cycle.
+ kBlockedOnUI // The CPU thread is waiting for a dialog to be handled.
+};
+
+
+// ---------------------------------------------------------------------------
+#pragma mark EmStopMethod
+// ---------------------------------------------------------------------------
+
+/*
+** An enumerated value passed to EmSessionStopper (or SuspendThread if it's
+** called directly) describing the way in which the thread should be
+** suspended.
+*/
+
+enum EmStopMethod
+{
+ kStopNone, // Don't stop the CPU.
+
+ kStopNow, // Suspend now, no matter what.
+
+ kStopOnCycle, // Suspend at the end of a cycle. This request may
+ // fail if the CPU thread is blocked on the UI.
+
+ kStopOnSysCall // Suspend on the next call to a system function. This
+ // request may fail if the CPU thread is block on the
+ // UI. The request may *hang* if the CPU thread is in
+ // a state such that it never reaches a system call.
+};
+
+
+// ---------------------------------------------------------------------------
+#pragma mark EmButtonEvent
+// ---------------------------------------------------------------------------
+
+/*
+** Struct containing all data for a button event (that is, when the user
+** clicks the mouse on a hard button, or presses an FKEY bound to one of
+** the hard buttons).
+*/
+
+struct EmButtonEvent
+{
+ EmButtonEvent (SkinElementType button, Bool isDown) :
+ fButton (button),
+ fButtonIsDown (isDown)
+ {
+ }
+
+ SkinElementType fButton;
+ Bool fButtonIsDown;
+};
+
+typedef EmThreadSafeQueue<EmButtonEvent> EmButtonQueue;
+
+
+// ---------------------------------------------------------------------------
+#pragma mark EmKeyEvent
+// ---------------------------------------------------------------------------
+
+/*
+** Struct containing all data for a key event.
+*/
+
+struct EmKeyEvent
+{
+ EmKeyEvent (uint16 ch) :
+ fKey (ch),
+ fShiftDown (0),
+ fCapsLockDown (0),
+ fNumLockDown (0),
+ fCommandDown (0),
+ fOptionDown (0),
+ fControlDown (0),
+ fAltDown (0),
+ fWindowsDown (0)
+ {}
+
+ uint16 fKey;
+ Bool fShiftDown;
+ Bool fCapsLockDown;
+ Bool fNumLockDown;
+ Bool fCommandDown;
+ Bool fOptionDown;
+ Bool fControlDown;
+ Bool fAltDown;
+ Bool fWindowsDown;
+};
+
+typedef EmThreadSafeQueue<EmKeyEvent> EmKeyQueue;
+
+
+// ---------------------------------------------------------------------------
+#pragma mark EmPenEvent
+// ---------------------------------------------------------------------------
+
+/*
+** Struct containing all data for a pen event (that is, when the user clicks
+** the mouse in the touchscreen area).
+*/
+
+struct EmPenEvent
+{
+ EmPenEvent (const EmPoint& point, Bool isDown) :
+ fPenPoint (point),
+ fPenIsDown (isDown)
+ {
+ }
+
+ EmPoint fPenPoint;
+ Bool fPenIsDown;
+
+ bool operator==(const EmPenEvent& other) const
+ {
+ return fPenPoint == other.fPenPoint &&
+ fPenIsDown == other.fPenIsDown;
+ }
+};
+
+typedef EmThreadSafeQueue<EmPenEvent> EmPenQueue;
+
+
+// ---------------------------------------------------------------------------
+#pragma mark Instruction/DataBreak
+// ---------------------------------------------------------------------------
+
+/*
+** Data types and collections for managing functions that install, remove,
+** and respond to instruction and data breaks.
+**
+** Instruction breaks occur when the PC reaches an indicated memory location.
+** Data breaks occur when an indicated memory location is accessed (either
+** read or write). An instruction or instruction data fetch does not trigger
+** a data break. (!!! Actually, I think the latter does...should it?)
+*/
+
+typedef void (*InstructionBreakInstaller) (void);
+typedef void (*InstructionBreakRemover) (void);
+typedef void (*InstructionBreakReacher) (void);
+
+struct InstructionBreakFuncs
+{
+ InstructionBreakInstaller fInstaller;
+ InstructionBreakRemover fRemover;
+ InstructionBreakReacher fReacher;
+};
+
+typedef vector<InstructionBreakFuncs> InstructionBreakFuncList;
+
+
+typedef void (*DataBreakInstaller) (void);
+typedef void (*DataBreakRemover) (void);
+typedef void (*DataBreakReacher) (emuptr, int size, Bool forRead);
+
+struct DataBreakFuncs
+{
+ DataBreakInstaller fInstaller;
+ DataBreakRemover fRemover;
+ DataBreakReacher fReacher;
+};
+
+typedef vector<DataBreakFuncs> DataBreakFuncList;
+
+
+// ---------------------------------------------------------------------------
+#pragma mark EmSession
+// ---------------------------------------------------------------------------
+
+class EmCPU;
+class EmDeferredErr;
+class SessionFile;
+struct Configuration;
+
+typedef vector<EmDeferredErr*> EmDeferredErrList;
+
+class EmSession;
+extern EmSession* gSession;
+
+class EmSession
+{
+ public:
+ // -----------------------------------------------------------------------------
+ // constructor / destructor
+ // -----------------------------------------------------------------------------
+
+ EmSession (void);
+ ~EmSession (void);
+
+ // -----------------------------------------------------------------------------
+ // public methods
+ // -----------------------------------------------------------------------------
+
+ // Methods to create a new session. One of these methods must be called after
+ // creation the EmSession object. CreateNew creates a new session based on
+ // the data in Configuration. CreateOld reads a previously saved session from
+ // the given file. CreateBound reads a previously saved session from resources
+ // bound to the file.
+ //
+ // After a session has been created, the client needs to call CreateThread.
+ // If "true" is passed to CreateThread, the client also needs to call
+ // ResumeThread. (On the Mac, it only makes sense to pass "false".)
+
+ void CreateNew (const Configuration&);
+ void CreateOld (const EmFileRef&);
+ void CreateBound (void);
+
+ // Standard sub-system methods:
+ // Reset: Resets the state. Called on hardware resets or on
+ // calls to SysReset. Also called from constructor.
+ // Save: Saves the state to the given file.
+ // Load: Loads the state from the given file. Can assume that
+ // Reset has been called first.
+
+ void Reset (EmResetType);
+ void Save (SessionFile&);
+ void Load (SessionFile&);
+
+ // Utility methods that create a SessionFile for the given file, and then
+ // call the above Save and Load methods. If updateFileRef is true, then
+ // the EmFileRef is remembered as part of the session's "identity".
+
+ void Save (const EmFileRef&,
+ Bool updateFileRef);
+ void Load (const EmFileRef&);
+
+ // Called by external thread to create and destroy the thread. CreateThread
+ // is called after the EmSession is created. If "suspended" is true, the
+ // client should also call ResumeThread. If "suspended" is false, the
+ // thread will start running immediately. DestroyThread can be called
+ // manually, but will also be called in the destructor (getting called
+ // more than once is OK).
+
+ void CreateThread (Bool suspended);
+ void DestroyThread (void);
+
+ // Called by external thread to suspend and resume the CPU thread. Not
+ // normally called directly by clients. Instead, clients should use
+ // a stack-based EmSessionStopper object.
+
+ Bool SuspendThread (EmStopMethod how);
+ void ResumeThread (void);
+
+#if HAS_OMNI_THREAD
+ // Pause the thread by the given number of milliseconds.
+
+ void Sleep (unsigned long msecs);
+
+ // Return whether or not the calling function is executing in the context of
+ // the CPU thread or not. If not, it's most likely executing in the UI
+ // thread -- much of Poser assumes this is the case.
+ //
+ // It's not clear if this function should return true or false on single-
+ // threaded systems, so it's not available to those.
+
+ Bool InCPUThread (void) const;
+#endif
+
+ // GetSessionState returns the state of the CPU thread, as described in the
+ // definition of the EmSessionState type. If the thread is suspended, you
+ // can call GetSuspendState to find out why. After a thread has been
+ // suspended, you might need to clear the reason for it being suspended
+ // by calling SetSuspendState before calling ResumeThread.
+ //
+ // These methods are protected by their own mutex, and can be called
+ // by any thread.
+
+ EmSessionState GetSessionState (void) const;
+ EmSuspendState GetSuspendState (void) const;
+ void SetSuspendState (const EmSuspendState&);
+
+ // Called on the Mac at idle time to execute a little bit then leave.
+ // Also called by SuspendThread when the CPU state needs to be halted
+ // on a system call or when the Palm OS is idle.
+ //
+ // Clients should check the suspend state in order to make sure that
+ // ExecuteIncremental exitted for the right reason. If the reason was
+ // fSuspendByTimeout, this flag will automatically be cleared before
+ // the function returns. If the reason was fSuspendByDebugger, then
+ // the debugger will have already been contacted.
+ //
+ // If the suspend state is non-zero when this function is called, the
+ // function merely exits immediately.
+
+ void ExecuteIncremental (void);
+
+ // Called by mechanisms that allow the calling of Palm OS system functions
+ // as if they were Poser subroutines.
+ //
+ // Clients should check the suspend state in order to make sure that
+ // ExecuteSubroutine exitted for the right reason.
+ //
+ // If the suspend state is non-zero when this function is called, the
+ // function saves that state, clears the suspend flags, and then enters
+ // the CPU loop. The previous suspend state flags are restored on
+ // exit. If any benign suspend flags were set while executing the
+ // Palm OS subroutine (for instance, fSuspendByTimeout), those flags
+ // are applied to the previously-saved flags before they are restored.
+
+ void ExecuteSubroutine (void);
+
+ // Called by the CPU thread if any processor-independent "end of cycle"
+ // processing needs to be carried out (for instance, Gremlins needs to
+ // save a snapshot or needs to switch to a previously saved state).
+ //
+ // This method is also responsible for resetting the emulation state
+ // (either in response to a hardware reset or a call to SysReset).
+ // "checkForResetOnly" is true if *only* this responsibility should
+ // be carried out and any other duties should be skipped. If it's
+ // false, then all end-of-cycle duties should be carried out.
+
+ Bool ExecuteSpecial (Bool checkForResetOnly);
+
+ Bool CheckForBreak (void);
+
+ // Called by the CPU thread when it needs to display an error message.
+ // Do NOT call this from any other thread.
+
+ EmDlgItemID BlockOnDialog (EmDlgThreadFn fn,
+ const void* parameters);
+
+#if HAS_OMNI_THREAD
+ // Called by the UI thread when the dialog has been dismissed, or
+ // if a thread blocked on the UI needs to be stopped. In the
+ // former case, dialogResult should be set to the button that
+ // dismissed the dialog. In the latter case, dialogResult should
+ // be set to -1.
+
+ void UnblockDialog (void);
+#endif
+
+ // Methods for interacting with the UI:
+ //
+ // PostFooEvent:
+ // Called by the UI thread to post UI events. Clients need not
+ // synchronize with the CPU thread first. These methods will do
+ // whatever is needed.
+ //
+ // HasFooEvent:
+ // Returns true if there's an event posted to the queue.
+ //
+ // PeekFooEvent:
+ // Returns the top event on the queue, but doesn't remove it
+ // from the queue.
+ //
+ // GetFooEvent:
+ // Returns the top event from the queue and removes it from
+ // the queue.
+ //
+ // These methods are protected by their own mutex, and can be called
+ // by any thread.
+
+ void PostButtonEvent (const EmButtonEvent&, Bool postNow = false);
+ Bool HasButtonEvent (void);
+ EmButtonEvent PeekButtonEvent (void);
+ EmButtonEvent GetButtonEvent (void);
+
+ void PostKeyEvent (const EmKeyEvent&);
+ Bool HasKeyEvent (void);
+ EmKeyEvent PeekKeyEvent (void);
+ EmKeyEvent GetKeyEvent (void);
+
+ void PostPenEvent (const EmPenEvent&);
+ Bool HasPenEvent (void);
+ EmPenEvent PeekPenEvent (void);
+ EmPenEvent GetPenEvent (void);
+
+ void ReleaseBootKeys (void);
+
+ // Method for getting information on the device we're emulating.
+ // Callable at any time from any thread.
+
+ Configuration GetConfiguration (void);
+ EmFileRef GetFile (void);
+ EmDevice GetDevice (void);
+
+ // Methods for determining if emulation should halt at certain
+ // points. Called inside the CPU thread.
+
+ Bool GetBreakOnSysCall (void);
+
+ // Called by the CPU thread to determine if emulation code is being
+ // executed as part of normal emulation, or as part of a subroutine
+ // call into the Palm OS. Certain features may be enable or disabled
+ // as a result of this check (for instance, breakpoints are generally
+ // disabled when Poser is making a Palm OS subroutine call).
+
+ Bool IsNested (void);
+
+ // Methods for determining if post-load operations need to take
+ // place. Called inside the CPU thread.
+
+ Bool GetNeedPostLoad (void);
+ void SetNeedPostLoad (Bool);
+
+ // Schedule for the CPU thread to suspend. Called inside the CPU thread.
+
+ void ScheduleSuspendException (void);
+ void ScheduleSuspendError (void);
+ void ScheduleSuspendExternal (void);
+ void ScheduleSuspendTimeout (void);
+ void ScheduleSuspendSysCall (void);
+ void ScheduleSuspendSubroutineReturn (void);
+
+ void ScheduleResumeExternal (void);
+
+ // Schedule for stuff to happen at the end of the current instruction
+ // cycle. Called inside the CPU thread.
+
+ void ScheduleReset (EmResetType);
+ void ScheduleResetBanks (void);
+ void ScheduleAutoSaveState (void);
+ void ScheduleSaveRootState (void);
+ void ScheduleSaveSuspendedState (void);
+ void ScheduleLoadRootState (void);
+ void ScheduleNextGremlinFromRootState (void);
+ void ScheduleNextGremlinFromSuspendedState (void);
+ void ScheduleMinimizeLoadState (void);
+ void ScheduleDeferredError (EmDeferredErr*);
+
+ void ClearDeferredErrors (void);
+
+ // Provide routines that get called when it's appropriate to install an
+ // instruction break, when it's appropriate to remove an instruction break,
+ // or when an instruction break has been reached. Called by various
+ // sub-systems in their Initialize methods.
+
+ void AddInstructionBreakHandlers (InstructionBreakInstaller,
+ InstructionBreakRemover,
+ InstructionBreakReacher);
+
+ // Provide routines that get called when it's appropriate to install a
+ // data break, when it's appropriate to remove a data break, or when a
+ // data break has been reached. Called by various sub-systems in their
+ // Initialize methods.
+
+ void AddDataBreakHandlers (DataBreakInstaller,
+ DataBreakRemover,
+ DataBreakReacher);
+
+ // Call the installed routines to install/remove the breakpoints.
+
+ void InstallInstructionBreaks (void);
+ void RemoveInstructionBreaks (void);
+ void HandleInstructionBreak (void);
+
+ void InstallDataBreaks (void);
+ void RemoveDataBreaks (void);
+ void HandleDataBreak (emuptr, int size, Bool forRead);
+
+ private:
+ // Initialize the state of the session and all of it's sub-systems based
+ // on the given configuration. The configuration is also saved and can
+ // later be queried if needed.
+
+ void Initialize (const Configuration&);
+
+ // Dispose of all of our resources, as well as all of our sub-systems'.
+ // Called from the destructor (after first stopping the CPU thread).
+
+ void Dispose (void);
+
+#if HAS_OMNI_THREAD
+ // -----------------------------------------------------------------------------
+ // omni_thread overrides
+ // -----------------------------------------------------------------------------
+
+ static void RunStatic (void* arg);
+ void Run (void);
+#endif
+
+ void CallCPU (void);
+
+ private:
+ friend class EmCPU;
+ friend class EmCPU68K; // Accesses fSuspendState and fStop directly.
+
+ private:
+ Configuration fConfiguration;
+ EmFileRef fFile;
+
+ EmCPU* fCPU;
+
+#if HAS_OMNI_THREAD
+ // Accessed from external thread only.
+
+ omni_thread* fThread;
+
+ // Accessed from any thread.
+
+ mutable omni_mutex fSharedLock;
+ mutable omni_condition fSharedCondition;
+
+ omni_mutex fSleepLock;
+ omni_condition fSleepCondition;
+#endif
+
+ // ----------------------------------------------------------------------
+ // The variables in this section are protected by the mutex, and should
+ // not be accessed without first acquiring it.
+ // ----------------------------------------------------------------------
+
+#if HAS_OMNI_THREAD
+ // Set to true if the thread should quit. If set by an external
+ // thread, the CPU thread will notice the request, but not immediately.
+ // If set internally, the CPU thread should also set SPCFLAG_EXIT of
+ // regs.spcflags in order for it to get noticed.
+
+ Bool fStop;
+#endif
+
+ // Set to non-zero if the thread should pause. If set by an external
+ // thread, the CPU thread will notice the request, but not immediately.
+ // If set internally, the CPU thread should also set SPCFLAG_SUSPEND of
+ // regs.spcflags in order for it to get noticed.
+
+ EmSuspendState fSuspendState;
+
+ // Set to kStopped when the thread is about to quit.
+ //
+ // Set to kSuspended when the thread is about to suspend.
+ //
+ // Set to kBlockedOnUI when thread is waiting for UI thread
+ // to display a dialog. UI thread should set this
+ // to false after the dialog is displayed and dismissed.
+ //
+ // Otherwise, set to kRunning.
+
+ EmSessionState fState;
+
+ // These values are read in any thread at any time, but should only
+ // be changed after stopping the CPU thread. They're set up by the
+ // ExecuteUntilFoo methods.
+
+ Bool fBreakOnSysCall;
+ int fNestLevel;
+
+ Bool fNeedPostLoad;
+
+ // These values indicate that certain end-of-cycle actions
+ // should take place.
+
+ Bool fReset;
+ Bool fResetBanks;
+ Bool fHordeAutoSaveState;
+ Bool fHordeSaveRootState;
+ Bool fHordeSaveSuspendState;
+ Bool fHordeLoadRootState;
+ Bool fHordeNextGremlinFromRootState;
+ Bool fHordeNextGremlinFromSuspendState;
+ Bool fMinimizeLoadState;
+
+ EmDeferredErrList fDeferredErrs;
+
+ EmResetType fResetType;
+
+ private:
+ EmButtonQueue fButtonQueue;
+ EmKeyQueue fKeyQueue;
+ EmPenQueue fPenQueue;
+
+ EmPenEvent fLastPenEvent;
+ uint32 fBootKeys;
+
+ private:
+ InstructionBreakFuncList fInstructionBreakFuncs;
+ DataBreakFuncList fDataBreakFuncs;
+};
+
+
+// ---------------------------------------------------------------------------
+#pragma mark EmSessionStopper
+// ---------------------------------------------------------------------------
+
+/*
+** Stack object used to briefly stop the emulator.
+*/
+
+class EmSessionStopper
+{
+ public:
+ EmSessionStopper (EmSession*, EmStopMethod how);
+ ~EmSessionStopper (void);
+
+ Bool Stopped (void);
+ Bool CanCall (void);
+
+ private:
+ EmSession* fSession;
+ int fHow;
+ Bool fStopped;
+};
+
+
+// Inlined because it's called from EmCPU68K::Execute on every opcode.
+
+inline Bool EmSession::IsNested (void)
+{
+ return fNestLevel > 0;
+}
+
+#endif /* EmSession_h */