/* * ldp.h * * Copyright (C) 2001 Matt Ownby * * This file is part of DAPHNE, a laserdisc arcade game emulator * * DAPHNE 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. * * DAPHNE is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef LDP_PRE_H #define LDP_PRE_H // different states that the laserdisc player could be in enum { LDP_ERROR, LDP_SEARCHING, LDP_STOPPED, LDP_PLAYING, LDP_PAUSED }; // different non-blocking search results enum { SEARCH_FAIL, // search failed SEARCH_SUCCESS, // search succeeded SEARCH_BUSY // search is still taking place, no change yet, my frenid }; #include // needed for datatypes // for bug logging #include #include using namespace std; #ifdef WIN32 #pragma warning (disable:4786) // disable warning about truncating to 255 in debug info #endif #define FRAME_SIZE 7 // rdg2010 // the size to make your frame array (the frame + the NULL terminator) #define FRAME_ARRAY_SIZE FRAME_SIZE + 1 class ldp { public: ldp(); virtual ~ldp(); // Call this function to initialize the ldp (the constructor does _not_ call it). // It's safe to call this function mulitple times. bool pre_init(); // player-specific init stuff // NOTE : this function should not be called directly, call pre_init instead virtual bool init_player(); // Call this function to shutdown the ldp (the destructor also calls it). // It's safe to call this function multiple times. void pre_shutdown(); // player-specific shutdown stuff // NOTE : this function should not be called directly, call pre_shutdown instead virtual void shutdown_player(); void clear(); // clears any received frames // searches to the 5-digit ASCII frame specified by 'pszFrame' // if 'block_until_search_finished' is true, this function will return after the search has completed and will return // true if the search was successful or false on error. // if 'block_until_search_finished' is false, this function will return after the search has _begun_ and will return true // if the player is currently executing the search or false if there was an error // Call this instead of calling the search function directly! // WARNING : if 'block_until_search_finished' is false, then the calling function // MUST also call get_status() to determine when a seek has finished. // If you aren't prepared to do this, make 'block_until_search_finished' true. bool pre_search(const char *, bool block_until_search_finished); // does the actual search. This func returns once the search has begun and you need to call get_search_result // until the search isn't busy anymore. // This function returns true if the LDP acknowledged the search virtual bool nonblocking_search(char *); // do not call this function directly! (get get_status instead) // This is the LDP-specific function that checks to see if a search (issued previously) finished // it returns one of the SEARCH_* enumerations ... virtual int get_search_result(); bool pre_skip_forward(Uint32); bool pre_skip_backward(Uint32); // steps 1 frame forward void pre_step_forward(); // steps 1 frame backward void pre_step_backward(); virtual bool skip_forward(Uint32 frames_to_skip, Uint32 target_frame); // NOTE : frames_to_skip and target_frame are both included for your convenience // use frames_to_skip only if you cannot use target_frame (which is more accurate) virtual bool skip_backward(Uint32 frames_to_skip, Uint32 target_frame); // NOTE : frames_to_skip and target_frame are both included for your convenience // use frames_to_skip only if you cannot use target_frame (which is more accurate) void pre_play(); void pre_pause(); void pre_stop(); // Function all 'ldp-in' drivers should call to change the playback speed // Input is a fraction (numerator/denominator) to avoid using a floating point value, // which doesn't work well on the GP2X. // Common speeds are 1X (1/1), 2X (2/1), 3X (3/1), 4X (4/1), 8X (8/1), // 1/2X (1/2), 1/3X (1/3), 1/4X (1/4), 1/8X (1/8) // Any weird speeds such as 7/2 have undefined behavior for now. // Returns true if the speed has changed or false if the speed cannot be changed bool pre_change_speed(unsigned int uNumerator, unsigned int uDenominator); // LASERDISC-PLAYER-SPECIFIC IMPLEMENTATIONS OF VARIOUS FUNCTIONALITY virtual unsigned int play(); virtual void pause(); virtual void stop(); // Implementation-specific version of pre_change_speed // Returns true if the speed has changed or false if the speed cannot be changed virtual bool change_speed(unsigned int uNumerator, unsigned int uDenominator); // function that can replace make_delay when no cpu is present and thinking is required void think_delay(unsigned int); // When emulating a cpu, this function MUST be called EVERY 1 ms (according to cpu's reckoning) to keep us in sync w/ the cpu. // This allows the cpu to do all timing calculations, which saves us from redundantly doing them again. // (when not emulating a cpu, this function can be called less often, every frame for example, and we will do the calculations ourselves) // This function updates the current frame so you cannot rely on get_current_frame to know when to call this function!!! void pre_think(); // pre_think() calls think() for ldp-specific stuff virtual void think(); // returns the current frame number that the disc is on // this is a generic function which computes the current frame number using the elapsed time // and the framerate of the disc. Obviously querying the laserdisc player would be preferable // if possible (some laserdisc players don't like to be queried too often) // SEEKING NOTES: // if get_current_frames() is called during a NON-BLOCKING seek, // it returns the frame the LDP was at right // when seek was begun. It DOES NOT check to see if a seek is finished, which // means that any driver that uses non-blocking seeking MUST also call get_status() // to check for the completion of that seek. // A laserdisc driver that supports non-blocking seeking (LD-V1000) SHOULD call // get_status() to check to see if a search is complete. If an LDP isn't designed // to return its status (ie the PR-8210) then either blocking seeking must be used // or each game driver which uses this player must call get_status(). virtual unsigned int get_current_frame(); // returns m_uCurrentFrame or m_uCurrentFrame+1 if the disc is playing and we've already displayed the 2nd field of the frame // (this is in an effort to fix overrun problems on super don) unsigned int get_adjusted_current_frame(); // returns 0 if this is the first vblank of the frame (assuming vblanks and frames line up), // or 1 if it's the second vblank of the frame unsigned int get_vblank_mini_count(); // causes sram to be saved after every seek virtual void set_sram_continuous_update(bool value); virtual void enable_audio1(); virtual void enable_audio2(); virtual void disable_audio1(); virtual void disable_audio2(); virtual void request_screenshot(); virtual void set_search_blanking(bool enabled); virtual void set_skip_blanking(bool enabled); virtual void set_seek_frames_per_ms(double value); virtual void set_min_seek_delay(unsigned int value); // END LDP-SPECIFIC SECTION bool is_vldp(); // returns true if our ldp type is VLDP bool is_blitting_allowed(); // returns value of blitting_allowed void set_blitting_allowed(bool bVal); int get_status(); // returns status of laserdisc player void framenum_to_frame(Uint32, char *); // converts int to 5-digit string Uint32 get_search_latency(); void set_search_latency(unsigned int); void set_stop_on_quit(bool); // enables the stop_on_quit bool flag Uint32 get_discvideo_height(); // gets the height of the laserdisc video (only meaningful with mpeg) Uint32 get_discvideo_width(); // gets the width of the laserdisc video (only meaningful with mpeg) virtual bool lock_overlay(Uint32); // prevents yuv callback from being called (only meaningful with mpeg) virtual bool unlock_overlay(Uint32); // sets the value of this boolean void set_use_nonblocking_searching(bool); // returns the value of this boolean bool get_use_nonblocking_searching(); unsigned int get_elapsed_ms_since_play(); // handles LDP-specific command-line arguments virtual bool handle_cmdline_arg(const char *arg); // Copies m_bug_log into 'log' and clears m_bug_log. // Used by releasetest. void get_bug_log(list &log); // debug function used by the cpu debugger void print_frame_info(); void setVerbose(bool); // rdg2010 protected: // helper function, shouldn't be called directly void increment_current_frame(); bool need_serial; // whether this LDP driver needs the serial port initialized bool serial_initialized; // whether serial has been initialized bool player_initialized; // whether the LDP has been properly initialized bool m_bIsVLDP; // this is true if our LDP type is VLDP bool blitting_allowed; // whether it's ok to blit directly to the screen (SMPEG forbids this) bool skipping_supported; // whether the laserdisc player supports skipping bool skip_instead_of_search; // whether we should skip instead of search if searching forward a short distance Uint32 max_skippable_frames; // maximum # of frames that player can skip (if skipping is supported) Uint32 m_last_try_frame; // the last frame we _tried_ to seek to Uint32 m_last_seeked_frame; // the last frame we successfully seeked to (used with m_play_time to calculate current frame) // UPDATE : we aren't using cycles anymore (see pre_think()) // Uint64 m_play_cycles; // # of elapsed cpu cycles from when we last issued a play command Uint32 m_play_time; // current time when we last issued a play command unsigned int m_start_time; // time when ldp() class was instantiated (only used when not using a cpu) int m_status; // the current status of the laserdisc player Uint32 search_latency; // how many ms to stall before searching (to simulate slow laserdisc players) bool m_stop_on_quit; // should the LDP stop when it quits? Uint32 m_discvideo_width; // width of laserdisc video (only meaningful with mpeg) Uint32 m_discvideo_height; // height of laserdisc video (only meaningful with mpeg) bool m_use_nonblocking_searching; // true if ldp-in drivers should use pre_search in non-blocking mode (as of now, blocking mode is more stable but non-blocking mode is more accurate) bool m_dont_get_search_result; // if we should not be calling get_search_result() bool m_sram_continuous_update; // if sram is to be updated on a regular basis // timer to be used to simulate search delay when in 'noldp' mode (for debugging) Uint32 m_noldp_timer; // used by 'releasetest' to do automatic self-testing list m_bug_log; unsigned int m_uCurrentFrame; // the current frame, as calculated by pre_think(), returned by get_current_frame() // current frame - m_last_seeked_frame (for VLDP's usage) // For example, the first frame displayed after playing is 0. unsigned int m_uCurrentOffsetFrame; // How many milliseconds have elapsed since we started playing the disc. // This value is changed by pre_think(), which must get called every 1 ms by the cpu loop // This allows us to keep in sync w/ the cpu w/o doing extra expensive calculations. unsigned int m_uElapsedMsSincePlay; // how many milliseconds have elapsed while we've been stuck doing blocking seeking // (blocking seeking is discouraged because it isn't proper emulation, but some real laserdisc players // need to use it for skipping, and a few functions like step-forward use it, // and it's probably not worth it to change that behavior as those functions are very rarely used) unsigned int m_uBlockedMsSincePlay; // if this is true, we won't increment m_uElapsedMsSincePlay until we get a vblank bool m_bWaitingForVblankToPlay; // the total offset of all frames we've skipped since play // (for example if I skipped +5 and then -10, this number would be -5) int m_iSkipOffsetSincePlay; // how many ms we have to surpass before increasing m_uCurrentOffsetFrame and m_uCurrentFrame unsigned int m_uMsFrameBoundary; // How many milliseconds have elapsed since Daphne started // This value is changed by pre_think() which is called every 1 ms by the cpu loop // This value is used to calculate how many emulated vblanks have occurred. unsigned int m_uElapsedMsSinceStart; // how many vblanks have occurred since Daphne started, as calculated by pre_think() unsigned int m_uVblankCount; // how many vblanks have occurred since last frame changed // (used if laserdisc FPS is half of vblank rate which is usually true) unsigned int m_uVblankMiniCount; // how many ms we have to surpass before increasing m_uVblankCount unsigned int m_uMsVblankBoundary; // How many frames to skip after displaying 1 frame (for playing at faster than 1X) unsigned int m_uFramesToSkipPerFrame; // How many frames to stall for after displaying 1 frame (for playing at slower than 1X) unsigned int m_uFramesToStallPerFrame; // State variable to keep track of whether we're stalling (according to m_uFramesToStallPerFrame) unsigned int m_uStallFrames; private: // set to true if pre_init has been called (used to error checking) bool m_bPreInitCalled; bool m_bVerbose; // Control message displays on daphne_log.txt -- rdg2010 }; // same as regular ldp class but has no seek or skip delay (testing skipping games with skip delay is super annoying) class fast_noldp : public ldp { // see ldp.h for comments on how to use these functions bool nonblocking_search(char *); int get_search_result(); bool skip_forward(Uint32 frames_to_skip, Uint32 target_frame); bool skip_backward(Uint32 frames_to_skip, Uint32 target_frame); }; extern ldp *g_ldp; // our global ldp class. Defined here so that every .cpp file doesn't have to define it. #endif