From daae5b7b9ef318af7c1c6fe3580f254b771799e6 Mon Sep 17 00:00:00 2001 From: Vincent Bousquet Date: Sat, 24 Aug 2024 21:16:58 +0200 Subject: [PATCH] Add data exchange through a global shared state block This commits allows to share previously unshared data without overloading too much the API by adding a single data block for pulsed state, device state, modulated segments, video displays, multiple DMDs with raw DMD frames (for correct rendering and colorization). This commit contains the skeleton and base implementation to set up the ground for more testing on client side (VPX) when support will be more advanced there. --- cmake/libpinmame/CMakeLists.txt | 1 + cmake/pinmame/CMakeLists_win-x64.txt | 1 + cmake/pinmame/CMakeLists_win-x86.txt | 1 + cmake/pinmame32/CMakeLists_win-x64.txt | 1 + cmake/pinmame32/CMakeLists_win-x86.txt | 1 + cmake/vpinmame/CMakeLists_win-x64.txt | 1 + cmake/vpinmame/CMakeLists_win-x86.txt | 1 + cmake/xpinmame/CMakeLists_linux-x64.txt | 1 + cmake/xpinmame/CMakeLists_osx-x64.txt | 1 + src/libpinmame/libpinmame.cpp | 28 +- src/libpinmame/libpinmame.h | 38 +- src/libpinmame/test.cpp | 26 +- src/win32com/Controller.cpp | 30 ++ src/win32com/Controller.h | 3 + src/win32com/ControllerDmdDevice.cpp | 6 +- src/win32com/VPinMAME.idl | 2 + src/wpc/byvidpin.c | 6 + src/wpc/core.c | 662 +++++++++++++++++++++++- src/wpc/core.h | 13 +- src/wpc/pinmamedef.h | 169 ++++++ src/wpc/sam.c | 8 +- 21 files changed, 953 insertions(+), 47 deletions(-) create mode 100644 src/wpc/pinmamedef.h diff --git a/cmake/libpinmame/CMakeLists.txt b/cmake/libpinmame/CMakeLists.txt index e2abb1ddf..b0bf9deab 100644 --- a/cmake/libpinmame/CMakeLists.txt +++ b/cmake/libpinmame/CMakeLists.txt @@ -504,6 +504,7 @@ set(PINMAME_SOURCES src/wpc/peyper.c src/wpc/peyper.h src/wpc/peypergames.c + src/wpc/pinmamedef.h src/wpc/play.c src/wpc/play.h src/wpc/playgames.c diff --git a/cmake/pinmame/CMakeLists_win-x64.txt b/cmake/pinmame/CMakeLists_win-x64.txt index 6b7852a99..754681a39 100644 --- a/cmake/pinmame/CMakeLists_win-x64.txt +++ b/cmake/pinmame/CMakeLists_win-x64.txt @@ -444,6 +444,7 @@ add_executable(pinmame WIN32 src/wpc/peyper.c src/wpc/peyper.h src/wpc/peypergames.c + src/wpc/pinmamedef.h src/wpc/play.c src/wpc/play.h src/wpc/playgames.c diff --git a/cmake/pinmame/CMakeLists_win-x86.txt b/cmake/pinmame/CMakeLists_win-x86.txt index ec4eddf47..5982b0df9 100644 --- a/cmake/pinmame/CMakeLists_win-x86.txt +++ b/cmake/pinmame/CMakeLists_win-x86.txt @@ -450,6 +450,7 @@ add_executable(pinmame WIN32 src/wpc/peyper.c src/wpc/peyper.h src/wpc/peypergames.c + src/wpc/pinmamedef.h src/wpc/play.c src/wpc/play.h src/wpc/playgames.c diff --git a/cmake/pinmame32/CMakeLists_win-x64.txt b/cmake/pinmame32/CMakeLists_win-x64.txt index d300c087c..7b1e03a3c 100644 --- a/cmake/pinmame32/CMakeLists_win-x64.txt +++ b/cmake/pinmame32/CMakeLists_win-x64.txt @@ -449,6 +449,7 @@ add_executable(pinmame32 WIN32 src/wpc/peyper.c src/wpc/peyper.h src/wpc/peypergames.c + src/wpc/pinmamedef.h src/wpc/play.c src/wpc/play.h src/wpc/playgames.c diff --git a/cmake/pinmame32/CMakeLists_win-x86.txt b/cmake/pinmame32/CMakeLists_win-x86.txt index f20acab77..01802ea0a 100644 --- a/cmake/pinmame32/CMakeLists_win-x86.txt +++ b/cmake/pinmame32/CMakeLists_win-x86.txt @@ -452,6 +452,7 @@ add_executable(pinmame32 WIN32 src/wpc/peyper.c src/wpc/peyper.h src/wpc/peypergames.c + src/wpc/pinmamedef.h src/wpc/play.c src/wpc/play.h src/wpc/playgames.c diff --git a/cmake/vpinmame/CMakeLists_win-x64.txt b/cmake/vpinmame/CMakeLists_win-x64.txt index a124e821c..38239a5d6 100644 --- a/cmake/vpinmame/CMakeLists_win-x64.txt +++ b/cmake/vpinmame/CMakeLists_win-x64.txt @@ -450,6 +450,7 @@ add_library(vpinmame SHARED src/wpc/peyper.c src/wpc/peyper.h src/wpc/peypergames.c + src/wpc/pinmamedef.h src/wpc/play.c src/wpc/play.h src/wpc/playgames.c diff --git a/cmake/vpinmame/CMakeLists_win-x86.txt b/cmake/vpinmame/CMakeLists_win-x86.txt index f5d7d623f..77e04aa76 100644 --- a/cmake/vpinmame/CMakeLists_win-x86.txt +++ b/cmake/vpinmame/CMakeLists_win-x86.txt @@ -453,6 +453,7 @@ add_library(vpinmame SHARED src/wpc/peyper.c src/wpc/peyper.h src/wpc/peypergames.c + src/wpc/pinmamedef.h src/wpc/play.c src/wpc/play.h src/wpc/playgames.c diff --git a/cmake/xpinmame/CMakeLists_linux-x64.txt b/cmake/xpinmame/CMakeLists_linux-x64.txt index a8246f5f2..fd10147c0 100644 --- a/cmake/xpinmame/CMakeLists_linux-x64.txt +++ b/cmake/xpinmame/CMakeLists_linux-x64.txt @@ -463,6 +463,7 @@ add_executable(xpinmame src/wpc/peyper.c src/wpc/peyper.h src/wpc/peypergames.c + src/wpc/pinmamedef.h src/wpc/play.c src/wpc/play.h src/wpc/playgames.c diff --git a/cmake/xpinmame/CMakeLists_osx-x64.txt b/cmake/xpinmame/CMakeLists_osx-x64.txt index f569b2201..655560483 100644 --- a/cmake/xpinmame/CMakeLists_osx-x64.txt +++ b/cmake/xpinmame/CMakeLists_osx-x64.txt @@ -453,6 +453,7 @@ add_executable(xpinmame src/wpc/peyper.c src/wpc/peyper.h src/wpc/peypergames.c + src/wpc/pinmamedef.h src/wpc/play.c src/wpc/play.h src/wpc/playgames.c diff --git a/src/libpinmame/libpinmame.cpp b/src/libpinmame/libpinmame.cpp index 283cbac6b..2d7589cf6 100644 --- a/src/libpinmame/libpinmame.cpp +++ b/src/libpinmame/libpinmame.cpp @@ -654,7 +654,7 @@ int StartGame(const int gameNum) * PinmameGetGame ******************************************************/ -PINMAMEAPI PINMAME_STATUS PinmameGetGame(const char* const p_name, PinmameGameCallback callback, const void* p_userData) +PINMAMEAPI PINMAME_STATUS PinmameGetGame(const char* const p_name, PinmameGameCallback callback, void* const p_userData) { if (!_p_Config) return PINMAME_STATUS_CONFIG_NOT_SET; @@ -686,7 +686,7 @@ PINMAMEAPI PINMAME_STATUS PinmameGetGame(const char* const p_name, PinmameGameCa * PinmameGetGames ******************************************************/ -PINMAMEAPI PINMAME_STATUS PinmameGetGames(PinmameGameCallback callback, const void* p_userData) +PINMAMEAPI PINMAME_STATUS PinmameGetGames(PinmameGameCallback callback, void* const p_userData) { if (!_p_Config) return PINMAME_STATUS_CONFIG_NOT_SET; @@ -886,6 +886,8 @@ PINMAMEAPI PINMAME_STATUS PinmameRun(const char* const p_name) if (gameNum < 0) return PINMAME_STATUS_GAME_NOT_FOUND; + OnStateChange(2); // Starting state + vp_init(); _p_gameThread = new std::thread(StartGame, gameNum); @@ -1407,7 +1409,25 @@ PINMAMEAPI int PinmameGetChangedNVRAM(PinmameNVRAMState* const p_nvramStates) * PinmameSetUserData ******************************************************/ -PINMAMEAPI void PinmameSetUserData(const void* p_userData) +PINMAMEAPI void PinmameSetUserData(void* const p_userData) { _p_userData = (void*)p_userData; -} \ No newline at end of file +} + +/****************************************************** + * PinmameGetStateBlock + ******************************************************/ + +PINMAMEAPI int PinmameGetStateBlock(const unsigned int updateMask, pinmame_tMachineOutputState** pp_outputState) +{ + if (!_isRunning) + return -1; + + pinmame_tMachineOutputState* p_outputState = (pinmame_tMachineOutputState*)core_getOutputState(updateMask); + if (!p_outputState) + return -1; + + *pp_outputState = p_outputState; + + return 0; +} diff --git a/src/libpinmame/libpinmame.h b/src/libpinmame/libpinmame.h index 33aa951e9..42b5d6ff9 100644 --- a/src/libpinmame/libpinmame.h +++ b/src/libpinmame/libpinmame.h @@ -17,6 +17,10 @@ #define PINMAME_MAX_MECHSW 20 #define PINMAME_ACCUMULATOR_SAMPLES 8192 // from mixer.c +#define UINT32 uint32_t +#define UINT8 uint8_t +#include "wpc/pinmamedef.h" + typedef enum { PINMAME_LOG_LEVEL_DEBUG = 0, PINMAME_LOG_LEVEL_INFO = 1, @@ -394,19 +398,19 @@ typedef struct { unsigned int standardcode; } PinmameKeyboardInfo; -typedef void (PINMAMECALLBACK *PinmameGameCallback)(PinmameGame* p_game, const void* p_userData); -typedef void (PINMAMECALLBACK *PinmameOnStateUpdatedCallback)(int state, const void* p_userData); -typedef void (PINMAMECALLBACK *PinmameOnDisplayAvailableCallback)(int index, int displayCount, PinmameDisplayLayout* p_displayLayout, const void* p_userData); -typedef void (PINMAMECALLBACK *PinmameOnDisplayUpdatedCallback)(int index, void* p_displayData, PinmameDisplayLayout* p_displayLayout, const void* p_userData); -typedef int (PINMAMECALLBACK *PinmameOnAudioAvailableCallback)(PinmameAudioInfo* p_audioInfo, const void* p_userData); -typedef int (PINMAMECALLBACK *PinmameOnAudioUpdatedCallback)(void* p_buffer, int samples, const void* p_userData); -typedef void (PINMAMECALLBACK *PinmameOnMechAvailableCallback)(int mechNo, PinmameMechInfo* p_mechInfo, const void* p_userData); -typedef void (PINMAMECALLBACK *PinmameOnMechUpdatedCallback)(int mechNo, PinmameMechInfo* p_mechInfo, const void* p_userData); -typedef void (PINMAMECALLBACK *PinmameOnSolenoidUpdatedCallback)(PinmameSolenoidState* p_solenoidState, const void* p_userData); -typedef void (PINMAMECALLBACK *PinmameOnConsoleDataUpdatedCallback)(void* p_data, int size, const void* p_userData); -typedef int (PINMAMECALLBACK *PinmameIsKeyPressedFunction)(PINMAME_KEYCODE keycode, const void* p_userData); -typedef void (PINMAMECALLBACK *PinmameOnLogMessageCallback)(PINMAME_LOG_LEVEL logLevel, const char* format, va_list args, const void* p_userData); -typedef void (PINMAMECALLBACK *PinmameOnSoundCommandCallback)(int boardNo, int cmd, const void* p_userData); +typedef void (PINMAMECALLBACK *PinmameGameCallback)(PinmameGame* p_game, void* const p_userData); +typedef void (PINMAMECALLBACK *PinmameOnStateUpdatedCallback)(int state, void* const p_userData); +typedef void (PINMAMECALLBACK *PinmameOnDisplayAvailableCallback)(int index, int displayCount, PinmameDisplayLayout* p_displayLayout, void* const p_userData); +typedef void (PINMAMECALLBACK *PinmameOnDisplayUpdatedCallback)(int index, void* p_displayData, PinmameDisplayLayout* p_displayLayout, void* const p_userData); +typedef int (PINMAMECALLBACK *PinmameOnAudioAvailableCallback)(PinmameAudioInfo* p_audioInfo, void* const p_userData); +typedef int (PINMAMECALLBACK *PinmameOnAudioUpdatedCallback)(void* p_buffer, int samples, void* const p_userData); +typedef void (PINMAMECALLBACK *PinmameOnMechAvailableCallback)(int mechNo, PinmameMechInfo* p_mechInfo, void* const p_userData); +typedef void (PINMAMECALLBACK *PinmameOnMechUpdatedCallback)(int mechNo, PinmameMechInfo* p_mechInfo, void* const p_userData); +typedef void (PINMAMECALLBACK *PinmameOnSolenoidUpdatedCallback)(PinmameSolenoidState* p_solenoidState, void* const p_userData); +typedef void (PINMAMECALLBACK *PinmameOnConsoleDataUpdatedCallback)(void* p_data, int size, void* const p_userData); +typedef int (PINMAMECALLBACK *PinmameIsKeyPressedFunction)(PINMAME_KEYCODE keycode, void* const p_userData); +typedef void (PINMAMECALLBACK *PinmameOnLogMessageCallback)(PINMAME_LOG_LEVEL logLevel, const char* format, va_list args, void* const p_userData); +typedef void (PINMAMECALLBACK *PinmameOnSoundCommandCallback)(int boardNo, int cmd, void* const p_userData); typedef struct { const PINMAME_AUDIO_FORMAT audioFormat; @@ -426,8 +430,8 @@ typedef struct { PinmameOnSoundCommandCallback cb_OnSoundCommand; } PinmameConfig; -PINMAMEAPI PINMAME_STATUS PinmameGetGame(const char* const p_name, PinmameGameCallback callback, const void* p_userData); -PINMAMEAPI PINMAME_STATUS PinmameGetGames(PinmameGameCallback callback, const void* p_userData); +PINMAMEAPI PINMAME_STATUS PinmameGetGame(const char* const p_name, PinmameGameCallback callback, void* const p_userData); +PINMAMEAPI PINMAME_STATUS PinmameGetGames(PinmameGameCallback callback, void* const p_userData); PINMAMEAPI void PinmameSetConfig(const PinmameConfig* const p_config); PINMAMEAPI void PinmameSetPath(const PINMAME_FILE_TYPE fileType, const char* const p_path); PINMAMEAPI int PinmameGetCheat(); @@ -476,4 +480,6 @@ PINMAMEAPI void PinmameSetDIP(const int dipBank, const int value); PINMAMEAPI int PinmameGetMaxNVRAM(); PINMAMEAPI int PinmameGetNVRAM(PinmameNVRAMState* const p_nvramStates); PINMAMEAPI int PinmameGetChangedNVRAM(PinmameNVRAMState* const p_nvramStates); -PINMAMEAPI void PinmameSetUserData(const void* p_userData); +PINMAMEAPI void PinmameSetUserData(void* const p_userData); +PINMAMEAPI int PinmameGetStateBlock(const unsigned int updateMask, pinmame_tMachineOutputState** pp_outputState); + diff --git a/src/libpinmame/test.cpp b/src/libpinmame/test.cpp index 4555c334d..dffea106c 100644 --- a/src/libpinmame/test.cpp +++ b/src/libpinmame/test.cpp @@ -134,13 +134,13 @@ void DumpAlphanumeric(int index, UINT16* p_displayData, PinmameDisplayLayout* p_ printf("%s\n", output[row]); } -void PINMAMECALLBACK Game(PinmameGame* game, const void* p_userData) +void PINMAMECALLBACK Game(PinmameGame* game, void* const p_userData) { printf("Game(): name=%s, description=%s, manufacturer=%s, year=%s, flags=%lu, found=%d\n", game->name, game->description, game->manufacturer, game->year, (unsigned long)game->flags, game->found); } -void PINMAMECALLBACK OnStateUpdated(int state, const void* p_userData) +void PINMAMECALLBACK OnStateUpdated(int state, void* const p_userData) { printf("OnStateUpdated(): state=%d\n", state); @@ -161,7 +161,7 @@ void PINMAMECALLBACK OnStateUpdated(int state, const void* p_userData) PinmameSetMech(1, &mechConfig); } -void PINMAMECALLBACK OnDisplayAvailable(int index, int displayCount, PinmameDisplayLayout* p_displayLayout, const void* p_userData) +void PINMAMECALLBACK OnDisplayAvailable(int index, int displayCount, PinmameDisplayLayout* p_displayLayout, void* const p_userData) { printf("OnDisplayAvailable(): index=%d, displayCount=%d, type=%d, top=%d, left=%d, width=%d, height=%d, depth=%d, length=%d\n", index, @@ -175,7 +175,7 @@ void PINMAMECALLBACK OnDisplayAvailable(int index, int displayCount, PinmameDisp p_displayLayout->length); } -void PINMAMECALLBACK OnDisplayUpdated(int index, void* p_displayData, PinmameDisplayLayout* p_displayLayout, const void* p_userData) +void PINMAMECALLBACK OnDisplayUpdated(int index, void* p_displayData, PinmameDisplayLayout* p_displayLayout, void* const p_userData) { printf("OnDisplayUpdated(): index=%d, type=%d, top=%d, left=%d, width=%d, height=%d, depth=%d, length=%d\n", index, @@ -197,7 +197,7 @@ void PINMAMECALLBACK OnDisplayUpdated(int index, void* p_displayData, PinmameDis } } -int PINMAMECALLBACK OnAudioAvailable(PinmameAudioInfo* p_audioInfo, const void* p_userData) +int PINMAMECALLBACK OnAudioAvailable(PinmameAudioInfo* p_audioInfo, void* const p_userData) { printf("OnAudioAvailable(): format=%d, channels=%d, sampleRate=%.2f, framesPerSecond=%.2f, samplesPerFrame=%d, bufferSize=%d\n", p_audioInfo->format, @@ -209,17 +209,17 @@ int PINMAMECALLBACK OnAudioAvailable(PinmameAudioInfo* p_audioInfo, const void* return p_audioInfo->samplesPerFrame; } -int PINMAMECALLBACK OnAudioUpdated(void* p_buffer, int samples, const void* p_userData) +int PINMAMECALLBACK OnAudioUpdated(void* p_buffer, int samples, void* const p_userData) { return samples; } -void PINMAMECALLBACK OnSolenoidUpdated(PinmameSolenoidState* p_solenoidState, const void* p_userData) +void PINMAMECALLBACK OnSolenoidUpdated(PinmameSolenoidState* p_solenoidState, void* const p_userData) { printf("OnSolenoidUpdated: solenoid=%d, state=%d\n", p_solenoidState->solNo, p_solenoidState->state); } -void PINMAMECALLBACK OnMechAvailable(int mechNo, PinmameMechInfo* p_mechInfo, const void* p_userData) +void PINMAMECALLBACK OnMechAvailable(int mechNo, PinmameMechInfo* p_mechInfo, void* const p_userData) { printf("OnMechAvailable: mechNo=%d, type=%d, length=%d, steps=%d, pos=%d, speed=%d\n", mechNo, @@ -230,7 +230,7 @@ void PINMAMECALLBACK OnMechAvailable(int mechNo, PinmameMechInfo* p_mechInfo, co p_mechInfo->speed); } -void PINMAMECALLBACK OnMechUpdated(int mechNo, PinmameMechInfo* p_mechInfo, const void* p_userData) +void PINMAMECALLBACK OnMechUpdated(int mechNo, PinmameMechInfo* p_mechInfo, void* const p_userData) { printf("OnMechUpdated: mechNo=%d, type=%d, length=%d, steps=%d, pos=%d, speed=%d\n", mechNo, @@ -241,17 +241,17 @@ void PINMAMECALLBACK OnMechUpdated(int mechNo, PinmameMechInfo* p_mechInfo, cons p_mechInfo->speed); } -void PINMAMECALLBACK OnConsoleDataUpdated(void* p_data, int size, const void* p_userData) +void PINMAMECALLBACK OnConsoleDataUpdated(void* p_data, int size, void* const p_userData) { printf("OnConsoleDataUpdated: size=%d\n", size); } -int PINMAMECALLBACK IsKeyPressed(PINMAME_KEYCODE keycode, const void* p_userData) +int PINMAMECALLBACK IsKeyPressed(PINMAME_KEYCODE keycode, void* const p_userData) { return 0; } -void PINMAMECALLBACK OnLogMessage(PINMAME_LOG_LEVEL logLevel, const char* format, va_list args, const void* p_userData) +void PINMAMECALLBACK OnLogMessage(PINMAME_LOG_LEVEL logLevel, const char* format, va_list args, void* const p_userData) { char buffer[1024]; vsnprintf(buffer, sizeof(buffer), format, args); @@ -262,7 +262,7 @@ void PINMAMECALLBACK OnLogMessage(PINMAME_LOG_LEVEL logLevel, const char* format printf("ERROR: %s\n", buffer); } -void PINMAMECALLBACK OnSoundCommand(int boardNo, int cmd, const void* p_userData) +void PINMAMECALLBACK OnSoundCommand(int boardNo, int cmd, void* const p_userData) { printf("OnSoundCommand: boardNo=%d, cmd=%d\n", boardNo, cmd); } diff --git a/src/win32com/Controller.cpp b/src/win32com/Controller.cpp index b898894e7..d06fa3783 100644 --- a/src/win32com/Controller.cpp +++ b/src/win32com/Controller.cpp @@ -1734,6 +1734,36 @@ STDMETHODIMP CController::put_TimeFence(double timeInS) return S_OK; } +/**************************************************************************** + * IController.get_StateBlock: returns a shared memory block name which holds + * a global state block prepended by the memory block size as an unsigned int + ****************************************************************************/ +STDMETHODIMP CController::get_StateBlock(/*[out, retval]*/ BSTR* pVal) +{ + if (!pVal) + return E_POINTER; + if (!Machine) + return E_FAIL; + if (core_getOutputState(PINMAME_STATE_REQMASK_ALL) == NULL) + return E_FAIL; + CComBSTR bsStateSharedMemName(TEXT("Local\\VPinMameStateBlock")); + *pVal = bsStateSharedMemName.Detach(); + return S_OK; +} + +/**************************************************************************** + * IController.UpdateStateBlock: Update requested outputs of the global state + * block + ****************************************************************************/ +STDMETHODIMP CController::UpdateStateBlock(/*[in, defaultvalue(0x3F)]*/ unsigned int updateMask) +{ + if (!Machine) + return E_FAIL; + if (core_getOutputState(updateMask) == NULL) + return E_FAIL; + return S_OK; +} + /**************************************************************************** * IController.Version (read-only): gets the program version of VPM ****************************************************************************/ diff --git a/src/win32com/Controller.h b/src/win32com/Controller.h index 3d03987b6..0f64c75d1 100644 --- a/src/win32com/Controller.h +++ b/src/win32com/Controller.h @@ -205,6 +205,9 @@ DECLARE_PROTECT_FINAL_CONSTRUCT() STDMETHOD(put_ModOutputType)(/*[in]*/ int output, /*[in]*/ int no, /*[in]*/ int newVal); STDMETHOD(put_TimeFence)(/*[in]*/ double fenceIns); + + STDMETHOD(get_StateBlock)(/*[out, retval]*/ BSTR* pVal); + STDMETHOD(UpdateStateBlock)(/*[in, defaultvalue(0x1F)]*/ unsigned int updateMask); }; #endif // !defined(AFX_Controller_H__D2811491_40D6_4656_9AA7_8FF85FD63543__INCLUDED_) diff --git a/src/win32com/ControllerDmdDevice.cpp b/src/win32com/ControllerDmdDevice.cpp index 3c8c97a38..d42e63fd1 100644 --- a/src/win32com/ControllerDmdDevice.cpp +++ b/src/win32com/ControllerDmdDevice.cpp @@ -51,8 +51,8 @@ typedef void(*DmdDev_Render_16_Shades_t)(UINT16 width, UINT16 height, UINT8* fra typedef void(*DmdDev_Render_4_Shades_t)(UINT16 width, UINT16 height, UINT8* frame); typedef void(*DmdDev_Render_16_Shades_with_Raw_t)(UINT16 width, UINT16 height, UINT8* frame, UINT32 noOfRawFrames, UINT8* rawbuffer); typedef void(*DmdDev_Render_4_Shades_with_Raw_t)(UINT16 width, UINT16 height, UINT8* frame, UINT32 noOfRawFrames, UINT8* rawbuffer); -typedef void(*DmdDev_Render_PM_Alphanumeric_Frame_t)(core_segOverallLayout_t layout, const UINT16* const seg_data, const UINT16* const seg_data2); -typedef void(*DmdDev_Render_PM_Alphanumeric_Dim_Frame_t)(core_segOverallLayout_t layout, const UINT16* const seg_data, const char* const seg_dim, const UINT16* const seg_data2); +typedef void(*DmdDev_Render_PM_Alphanumeric_Frame_t)(core_tSegOverallLayout layout, const UINT16* const seg_data, const UINT16* const seg_data2); +typedef void(*DmdDev_Render_PM_Alphanumeric_Dim_Frame_t)(core_tSegOverallLayout layout, const UINT16* const seg_data, const char* const seg_dim, const UINT16* const seg_data2); typedef void(*DmdDev_Render_Lum_And_Raw_t)(UINT16 width, UINT16 height, UINT8* lumFrame, UINT8* rawFrame, UINT32 noOfRawFrames, UINT8* rawbuffer); typedef struct { @@ -239,7 +239,7 @@ extern "C" void dmddeviceRenderDMDFrame(const int width, const int height, UINT8 } } -extern "C" void dmddeviceRenderAlphanumericFrame(core_segOverallLayout_t layout, UINT16* seg_data, UINT16* seg_data2, char* seg_dim) { +extern "C" void dmddeviceRenderAlphanumericFrame(core_tSegOverallLayout layout, UINT16* seg_data, UINT16* seg_data2, char* seg_dim) { for (int i = 0; i < 2; i++) { if (dmdDevices[i].Render_PM_Alphanumeric_Dim_Frame) diff --git a/src/win32com/VPinMAME.idl b/src/win32com/VPinMAME.idl index 89e38553f..c70fe11cc 100644 --- a/src/win32com/VPinMAME.idl +++ b/src/win32com/VPinMAME.idl @@ -259,6 +259,8 @@ import "ocidl.idl"; [propget, id(88), helpstring("property ModOutputType")] HRESULT ModOutputType([in] int output, [in] int no, [out, retval] int *pVal); [propput, id(88), helpstring("property ModOutputType")] HRESULT ModOutputType([in] int output, [in] int no, [in] int newVal); [propput, id(89), helpstring("property TimeFence")] HRESULT TimeFence([in] double timeInS); + [propget, id(90), helpstring("property StateBlock")] HRESULT StateBlock([out, retval] BSTR* pVal); + [id(91), helpstring("method UpdateStateBlock")] HRESULT UpdateStateBlock([in, defaultvalue(0x3F)] unsigned int updateMask); }; // WSHDlg and related interfaces diff --git a/src/wpc/byvidpin.c b/src/wpc/byvidpin.c index 51135519e..2e4d6ec7e 100644 --- a/src/wpc/byvidpin.c +++ b/src/wpc/byvidpin.c @@ -459,6 +459,12 @@ static VIDEO_STOP(byVP) { static PINMAME_VIDEO_UPDATE(byVP_update) { TMS9928A_refresh((core_gameData->hw.display ? 2 : 1), bitmap, 1); + struct rectangle bounds; + bounds.min_x = 0; + bounds.min_y = 0; + bounds.max_x = 192; + bounds.max_y = 256; + core_display_video_update(bitmap, &bounds, layout, 1); return 0; } diff --git a/src/wpc/core.c b/src/wpc/core.c index f1b559758..8eda314c1 100644 --- a/src/wpc/core.c +++ b/src/wpc/core.c @@ -107,6 +107,9 @@ void vp_setDIP(int bank, int value) { } #ifndef MIN #define MIN(x,y) ((x)<(y)?(x):(y)) #endif +#ifndef MAX + #define MAX(x,y) ((x)>(y)?(x):(y)) +#endif INLINE UINT8 saturatedByte(float v) { @@ -732,7 +735,26 @@ static struct { UINT8 lastLampMatrix[CORE_MAXLAMPCOL]; int lastGI[CORE_MAXGI]; UINT64 lastSol; + /*-- Global output state block --*/ + pinmame_tMachineOutputState* outputStateBlock; // Global output state block, shared with clients of PinMame + struct { // Map between display layout (DMD/Video) and corresponding output frame + struct core_dispLayout* layout; + pinmame_tFrameState* frame; + pinmame_tFrameState* raw; + } displayStateBlocks[32]; + int nSortedSegLayout; // Amount of alphanumeric segment display + core_tLCDLayout sortedSegLayout[CORE_SEGCOUNT]; // Sorted individual segment element, each display, shares the same top value + struct + { + int type; // Bit0 = unused/wired, Bit1 = Solenoid/GI, Bit2 = core_getSol or coreGlobals.GI/coreGlobals.physicOutputState + int solIndex; // Solenoid index (note that this is 1 based, so 1..CORE_MODOUT_SOL_MAX) + int physOutIndex; // Physic Output index, see CORE_MODOUT_SOL0, CORE_MODOUT_GI0,... + int giIndex; // GI index (note that this is 0 based) + } controlledOutputMapping[CORE_MODOUT_SOL_MAX + CORE_MODOUT_GI_MAX]; // Map between state block output and internal GI/Sol/PhysicOutput (see core_getAllSol): Positive = physic output / Negative = index for core_getSol() or coreGlobals.gi if <= -1000 / Unwired if -2000 /*-- VPinMAME specifics --*/ + #if defined(VPINMAME) + HANDLE outputStateSharedMem; // handle to the shared memory block used to share output states + #endif #if defined(VPINMAME) || defined(LIBPINMAME) UINT8 vpm_dmd_last_lum[DMD_MAXY * DMD_MAXX]; UINT8 vpm_dmd_luminance_lut[256]; @@ -910,14 +932,14 @@ void core_dmd_capture_frame(const int width, const int height, const UINT8* cons // Note that this part of the header is not used externally of VPinMAME (move it to something like core_dmdevice.h/core_dmddevice.c ?) extern int dmddeviceInit(const char* GameName, UINT64 HardwareGeneration, const tPMoptions* Options); extern void dmddeviceRenderDMDFrame(const int width, const int height, UINT8* dmdDotLum, UINT8* dmdDotRaw, UINT32 noOfRawFrames, UINT8* rawbuffer, const int isDMD2); -extern void dmddeviceRenderAlphanumericFrame(core_segOverallLayout_t layout, UINT16* seg_data, UINT16* seg_data2, char* seg_dim); +extern void dmddeviceRenderAlphanumericFrame(core_tSegOverallLayout layout, UINT16* seg_data, UINT16* seg_data2, char* seg_dim); extern void dmddeviceFwdConsoleData(UINT8 data); extern void dmddeviceDeInit(void); #endif /* VPINMAME */ -core_segOverallLayout_t layoutAlphanumericFrame(UINT64 gen, UINT8 total_disp, UINT8 *disp_num_segs, const char* GameName) { +core_tSegOverallLayout layoutAlphanumericFrame(UINT64 gen, UINT8 total_disp, UINT8 *disp_num_segs, const char* GameName) { // TODO this should be moved to the driver's definition, or MACHINE_INIT, setting it once and for all at machine startup from core_gameData->lcdLayout - core_segOverallLayout_t layout = CORE_SEGLAYOUT_None; + core_tSegOverallLayout layout = CORE_SEGLAYOUT_None; // switch to current game tech switch(gen){ @@ -1234,7 +1256,7 @@ static void updateDisplay(struct mame_bitmap *bitmap, const struct rectangle *cl if(!has_DMD_Video) { // Identify alphaseg layout - const core_segOverallLayout_t alpha_layout = layoutAlphanumericFrame(core_gameData->gen, n_seg_layouts, disp_num_segs, Machine->gamedrv->name); + const core_tSegOverallLayout alpha_layout = layoutAlphanumericFrame(core_gameData->gen, n_seg_layouts, disp_num_segs, Machine->gamedrv->name); assert(alpha_layout != CORE_SEGLAYOUT_Invalid); // Port of legacy hack that would call updateDisplay twice, once with the default display (4x7 and 2x2) then once for the additional display (3x2) @@ -1995,6 +2017,540 @@ int core_getDip(int dipBank) { #endif /* VPINMAME */ } +/*------------------------------------------------- +/ Lazily create and update a mem block holding output state +/--------------------------------------------------*/ +void core_createStateBlock() +{ + assert(locals.outputStateBlock == NULL); + struct core_dispLayout* layout, * parent_layout; + + // Evaluate required data block size + const unsigned int headerSize = sizeof(pinmame_tMachineOutputState); + // Controlled output with their binary state + const unsigned int nControlledOutput = coreGlobals.nGI + (coreGlobals.nSolenoids ? coreGlobals.nSolenoids : (CORE_FIRSTCUSTSOL - 1 + core_gameData->hw.custSol)); + const unsigned int controlledOutputBinarySize = sizeof(pinmame_tBinaryStates) + (((MIN(32, nControlledOutput) / 8) + 3) & ~3); + const unsigned int controlledOutputDeviceSize = sizeof(pinmame_tDeviceStates) + nControlledOutput * sizeof(pinmame_tDeviceState); + // Lamps + unsigned int nLampDevices = 0; + { + const int hasSAMModulatedLeds = (core_gameData->gen & GEN_SAM) && (core_gameData->hw.lampCol > 2); + const int nLamps = (hasSAMModulatedLeds || coreGlobals.nLamps) ? coreGlobals.nLamps : (8 * (CORE_CUSTLAMPCOL + core_gameData->hw.lampCol)); + for (int i = 0; i < nLamps; i++) + { + const unsigned int lampNo = coreData->m2lamp ? coreData->m2lamp((i / 8) + 1, i & 7) : i; + nLampDevices = MAX(nLampDevices, lampNo); + } + } + const unsigned int lampMatrixDeviceSize = sizeof(pinmame_tDeviceStates) + nLampDevices * sizeof(pinmame_tDeviceState); + // DMD, Display, Raw DMD and alphanumeric segment displays + int nSegLayouts = 0; + core_tLCDLayout* segLayout[128] = { 0 }; + unsigned int displayStateSize = sizeof(pinmame_tDisplayStates); + unsigned int rawDisplayStateSize = sizeof(pinmame_tDisplayStates); + unsigned int alphaDisplayDeviceSize = sizeof(pinmame_tAlphaStates) + locals.nSortedSegLayout * sizeof(pinmame_tAlphaSegmentState); + for (layout = core_gameData->lcdLayout, parent_layout = NULL; layout->length || (parent_layout && parent_layout->length); layout += 1) { + if (layout->length == 0) { layout = parent_layout; parent_layout = NULL; } + switch (layout->type & CORE_SEGMASK) + { + case CORE_IMPORT: assert(parent_layout == NULL); parent_layout = layout + 1; layout = layout->lptr - 1; break; + case CORE_DMD: // DMD displays and LED matrices (for example RBION,... search for CORE_NODISP to list them) + rawDisplayStateSize += sizeof(pinmame_tFrameState) + layout->length * layout->start * 1; + case CORE_VIDEO: // Video display for games like Baby PacMan, frames are stored as RGB8 + displayStateSize += sizeof(pinmame_tFrameState) + layout->length * layout->start * 3; + break; + default: // Alphanumeric segment displays + alphaDisplayDeviceSize += layout->length * sizeof(pinmame_tAlphaSegmentState); + segLayout[nSegLayouts] = layout; + nSegLayouts++; + break; + } + } + // Layout declaration in drivers were originaly made for rendering and are (sadly) also used to unswizzle alphanum segment data. + // We need to interpret them to build back the displays list with their individual components (for example see Space Gambler). + // To do so, we convert them to individual elements and group them based on their declaration order and render position. + locals.nSortedSegLayout = 0; + memset(locals.sortedSegLayout, 0, sizeof(locals.sortedSegLayout)); + for (int i = 0; i < nSegLayouts; i++) + { + // Split layout into individual components, converting type to the state block enum, eventually applying forced commas and reversed order + for (int j = 0; j < segLayout[i]->length; j++) + { + assert(locals.nSortedSegLayout < CORE_SEGCOUNT); + memcpy(&locals.sortedSegLayout[locals.nSortedSegLayout], segLayout[i], sizeof(core_tLCDLayout)); + int type; + switch (locals.sortedSegLayout[locals.nSortedSegLayout].type & CORE_SEGALL) { + case CORE_SEG7: type = PINMAME_SEG_LAYOUT_7; break; + case CORE_SEG7S: type = PINMAME_SEG_LAYOUT_7; break; + case CORE_SEG7SC: type = PINMAME_SEG_LAYOUT_7F; break; + case CORE_SEG87F: type = ((j > 0) && (j % 3 == 0)) ? PINMAME_SEG_LAYOUT_7F : PINMAME_SEG_LAYOUT_7; break; + case CORE_SEG87: type = ((j > 0) && (j % 3 == 0)) ? PINMAME_SEG_LAYOUT_8 : PINMAME_SEG_LAYOUT_7; break; + case CORE_SEG8: type = PINMAME_SEG_LAYOUT_8; break; + case CORE_SEG8D: type = PINMAME_SEG_LAYOUT_8D; break; + case CORE_SEG9: type = PINMAME_SEG_LAYOUT_9; break; + case CORE_SEG10: type = PINMAME_SEG_LAYOUT_10; break; + case CORE_SEG98F: type = ((j > 0) && (j % 3 == 0)) ? PINMAME_SEG_LAYOUT_9F : PINMAME_SEG_LAYOUT_9; break; + case CORE_SEG98: type = ((j > 0) && (j % 3 == 0)) ? PINMAME_SEG_LAYOUT_10 : PINMAME_SEG_LAYOUT_9; break; + case CORE_SEG16N: type = PINMAME_SEG_LAYOUT_14; break; + case CORE_SEG16D: type = PINMAME_SEG_LAYOUT_15; break; + case CORE_SEG16: type = PINMAME_SEG_LAYOUT_16; break; + case CORE_SEG16R: type = PINMAME_SEG_LAYOUT_16R; break; + case CORE_SEG16S: type = PINMAME_SEG_LAYOUT_16S; break; + } + locals.sortedSegLayout[locals.nSortedSegLayout].type = type | (segLayout[i]->type & (CORE_SEGHIBIT | CORE_SEGREV)); + locals.sortedSegLayout[locals.nSortedSegLayout].left += j * 2; + locals.sortedSegLayout[locals.nSortedSegLayout].length = 1; + if (locals.sortedSegLayout[locals.nSortedSegLayout].type & CORE_SEGREV) + locals.sortedSegLayout[locals.nSortedSegLayout].start = locals.sortedSegLayout[locals.nSortedSegLayout].start + segLayout[i]->length - 1 - j; + else + locals.sortedSegLayout[locals.nSortedSegLayout].start = locals.sortedSegLayout[locals.nSortedSegLayout].start + j; + locals.nSortedSegLayout++; + } + } + int segDisplayStart = 0, nSegDisplays = 0; + for (int i = 0; i < locals.nSortedSegLayout; i++) + { + if ((i == locals.nSortedSegLayout - 1) // Last element + || (locals.sortedSegLayout[i].top != locals.sortedSegLayout[i + 1].top) // Next element is on another line + || (locals.sortedSegLayout[i].left + 2 != locals.sortedSegLayout[i + 1].left)) // Next element is not next to this one + // end of block could also be a change of element size (based on element type) but does not seems to be needed + { + for (int j = segDisplayStart; j <= i; j++) + { + locals.sortedSegLayout[j].top = nSegDisplays; + locals.sortedSegLayout[j].left = j - segDisplayStart; + locals.sortedSegLayout[j].length = i + 1 - segDisplayStart; + } + nSegDisplays++; + segDisplayStart = i + 1; + } + } + alphaDisplayDeviceSize += nSegDisplays * sizeof(pinmame_tAlphaDisplayDef); + // + const unsigned int size = headerSize + + controlledOutputBinarySize + + controlledOutputDeviceSize + + lampMatrixDeviceSize + + alphaDisplayDeviceSize + + displayStateSize + + rawDisplayStateSize; + + + #ifdef VPINMAME + // VPinMame uses Windows shared memory to share the state block, so we need to allocate & map it as such + static TCHAR szName[] = TEXT("Local\\VPinMameStateBlock"); + locals.outputStateSharedMem = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size + sizeof(unsigned int), szName); + if (locals.outputStateSharedMem == NULL) + return; + UINT8* sharedMem = (UINT8*)MapViewOfFile(locals.outputStateSharedMem, FILE_MAP_WRITE, 0, 0, size + sizeof(unsigned int)); + if (sharedMem == NULL) + { + CloseHandle(locals.outputStateSharedMem); + locals.outputStateSharedMem = INVALID_HANDLE_VALUE; + return; + } + *((unsigned int*)sharedMem) = size; + locals.outputStateBlock = (pinmame_tMachineOutputState*)(sharedMem + sizeof(unsigned int)); + #else + locals.outputStateBlock = (pinmame_tMachineOutputState*)malloc(size); + #endif + UINT8* data = (UINT8*)locals.outputStateBlock; + memset(data, 0, size); + + // Overall header. We use a version id here instead of the main PinMame version to allow changing it during development phase + locals.outputStateBlock->versionID = 1; + data += headerSize; + + // Controlled outputs: all general purpose controlled outputs (usually solenoids, but also flashers, motors, child boards, GI,...) with a fairly messy mapping + locals.outputStateBlock->controlledOutputBinaryStates = (pinmame_tBinaryStates*)data; + locals.outputStateBlock->controlledOutputBinaryStates->nOutputs = MIN(32, coreGlobals.nGI + (coreGlobals.nSolenoids ? coreGlobals.nSolenoids : (CORE_FIRSTCUSTSOL - 1 + core_gameData->hw.custSol))); + data += controlledOutputBinarySize; + locals.outputStateBlock->controlledDeviceStates = (pinmame_tDeviceStates*)data; + locals.outputStateBlock->controlledDeviceStates->nDevices = coreGlobals.nGI + (coreGlobals.nSolenoids ? coreGlobals.nSolenoids : (CORE_FIRSTCUSTSOL - 1 + core_gameData->hw.custSol)); + locals.outputStateBlock->controlledDeviceStates->stateByteSize = sizeof(pinmame_tDeviceState); + data += controlledOutputDeviceSize; + memset(locals.controlledOutputMapping, 0, sizeof(locals.controlledOutputMapping)); + if (coreGlobals.nSolenoids && (options.usemodsol & (CORE_MODOUT_ENABLE_PHYSOUT_SOLENOIDS | CORE_MODOUT_ENABLE_MODSOL))) + { + /*-- 1..32, hardware solenoids --*/ + if (core_gameData->gen & GEN_ALLWPC) { + for (int i = 0; i < 28; i++) + { + locals.controlledOutputMapping[i].type = 5; + locals.controlledOutputMapping[i].solIndex = i + 1; + locals.controlledOutputMapping[i].physOutIndex = CORE_MODOUT_SOL0 + i; + } + // 29..32 GameOn (not modulated, stored in 0x0F00 of solenoids2) + for (int i = 28; i < 32; i++) + { + locals.controlledOutputMapping[i].type = 1; + locals.controlledOutputMapping[i].solIndex = i + 1; // mapping from 29..32 to solenoids2 bit is done in core_getSol + } + } + else + for (int i = 0; i < 32; i++) + { + locals.controlledOutputMapping[i].type = 5; + locals.controlledOutputMapping[i].solIndex = i + 1; + locals.controlledOutputMapping[i].physOutIndex = CORE_MODOUT_SOL0 + i; + } + /*-- 33..36 upper flipper solenoids (not modulated for the time being) --*/ + for (int i = 32; i < 36; i++) + { + locals.controlledOutputMapping[i].type = 1; + locals.controlledOutputMapping[i].solIndex = i + 1; + } + /*-- 37..44, extra solenoids --*/ + if (core_gameData->gen & (GEN_WPC95 | GEN_WPC95DCS)) { // 37-44 WPC95 extra (duplicated 37..40 / 41..44) + for (int i = 36; i < 44; i++) + { + locals.controlledOutputMapping[i].type = 5; + locals.controlledOutputMapping[i].solIndex = 29 + ((i - 36) & 3); // Maps to 29..32 + locals.controlledOutputMapping[i].physOutIndex = CORE_MODOUT_SOL0 + 28 + ((i - 36) & 3); + } + } + else if (core_gameData->gen & (GEN_ALLS11 | GEN_SAM | GEN_SPA)) // 37-44 S11, SAM extra + for (int i = 36; i < 44; i++) + { + locals.controlledOutputMapping[i].type = 5; + locals.controlledOutputMapping[i].solIndex = i + 4; + locals.controlledOutputMapping[i].physOutIndex = CORE_MODOUT_SOL0 + i + 4; + } + /*-- 45..48 lower flipper solenoids (not modulated for the time being) --*/ + for (int i = 44; i < 48; i++) + { + locals.controlledOutputMapping[i].type = 1; + locals.controlledOutputMapping[i].solIndex = i + 1; + } + /*-- 49..50 simulated (50 is unused so far) --*/ + { + locals.controlledOutputMapping[48].type = 1; + locals.controlledOutputMapping[48].solIndex = 49; + } + /*-- 51..66 custom --*/ + for (int i = CORE_FIRSTCUSTSOL - 1; i < CORE_FIRSTCUSTSOL - 1 + core_gameData->hw.custSol; i++) + { + locals.controlledOutputMapping[i].type = i < coreGlobals.nSolenoids ? 5 : 1; + locals.controlledOutputMapping[i].solIndex = i + 1; + locals.controlledOutputMapping[i].physOutIndex = CORE_MODOUT_SOL0 + i; + } + } + else + { + for (int i = 0; i < CORE_FIRSTCUSTSOL + core_gameData->hw.custSol - 1; i++) + { + locals.controlledOutputMapping[i].type = 1; + locals.controlledOutputMapping[i].solIndex = i + 1; + } + } + /*-- WPC, Sega/Stern Whitestar and Stern SAM GI are added to the tail --*/ + // GI are controlled outputs dedicated to GI that WPC and Sega/Stern Whitestar drivers declare (other drivers do not declare them separately and treat them as regular controlled outputs) + // . 3 to 5 are declared for WPC with a 'modulated' value between 0 and 8 (legacy implementation is somewhat buggy regarding this value) + // . 1 is declared for Sega/Stern Whitestar and Stern SAM with a value of either 0 or 9 + for (int i = 0; i < coreGlobals.nGI; i++) + { + locals.controlledOutputMapping[CORE_FIRSTCUSTSOL - 1 + core_gameData->hw.custSol + i].type = (coreGlobals.nGI && (options.usemodsol & CORE_MODOUT_ENABLE_PHYSOUT_GI)) ? 7 : 3; + locals.controlledOutputMapping[CORE_FIRSTCUSTSOL - 1 + core_gameData->hw.custSol + i].giIndex = i; + locals.controlledOutputMapping[CORE_FIRSTCUSTSOL - 1 + core_gameData->hw.custSol + i].physOutIndex = CORE_MODOUT_GI0 + i; + } + for (unsigned int i = 0; i < locals.outputStateBlock->controlledDeviceStates->nDevices; i++) + { + const int outputType = locals.controlledOutputMapping[i].type; + if ((outputType & 5) == 1) // 'Legacy' Solenoid & GI + locals.outputStateBlock->controlledDeviceStates->states[i].category = PINMAME_DEVICE_STATE_TYPE_CUSTOM; + else if ((outputType & 5) == 5) // Physic Output Solenoid & GI + { + const int output = locals.controlledOutputMapping[i].physOutIndex; + const int type = coreGlobals.physicOutputState[output].type; + if (options.usemodsol & CORE_MODOUT_ENABLE_MODSOL) + locals.outputStateBlock->controlledDeviceStates->states[i].category = PINMAME_DEVICE_STATE_TYPE_CUSTOM; + else if (type >= CORE_MODOUT_BULB_44_6_3V_AC && type <= CORE_MODOUT_BULB_906_25V_DC_S11) + locals.outputStateBlock->controlledDeviceStates->states[i].category = PINMAME_DEVICE_STATE_TYPE_BULB; + else if (type >= CORE_MODOUT_LED && type <= CORE_MODOUT_VFD_STROBE_1_16MS) + locals.outputStateBlock->controlledDeviceStates->states[i].category = PINMAME_DEVICE_STATE_TYPE_LED; + else + locals.outputStateBlock->controlledDeviceStates->states[i].category = PINMAME_DEVICE_STATE_TYPE_CUSTOM; + } + } + + // Lamp matrix: strobed lamp matrix (but some drivers also uses this for additional lamps/outputs) + locals.outputStateBlock->lampMatrixStates = (pinmame_tDeviceStates*)data; + locals.outputStateBlock->lampMatrixStates->nDevices = 0; + const int hasSAMModulatedLeds = (core_gameData->gen & GEN_SAM) && (core_gameData->hw.lampCol > 2); + const int nLamps = (hasSAMModulatedLeds || coreGlobals.nLamps) ? coreGlobals.nLamps : (8 * (CORE_CUSTLAMPCOL + core_gameData->hw.lampCol)); + for (int i = 0; i < nLamps; i++) + { + const unsigned int lampNo = coreData->m2lamp ? coreData->m2lamp((i / 8) + 1, i & 7) : i; + locals.outputStateBlock->lampMatrixStates->nDevices = MAX(locals.outputStateBlock->lampMatrixStates->nDevices, lampNo); + assert(locals.outputStateBlock->lampMatrixStates->nDevices < (1 + CORE_MAXLAMPCOL * 8)); // This assert is needed since we can not guarantee that the driver will map the lamps inside the max range + if (coreGlobals.nLamps && (options.usemodsol & (CORE_MODOUT_ENABLE_PHYSOUT_LAMPS))) + { + const int type = coreGlobals.physicOutputState[CORE_MODOUT_LAMP0 + i].type; + if (type >= CORE_MODOUT_BULB_44_6_3V_AC && type <= CORE_MODOUT_BULB_906_25V_DC_S11) + locals.outputStateBlock->controlledDeviceStates->states[lampNo].category = PINMAME_DEVICE_STATE_TYPE_BULB; + else if (type >= CORE_MODOUT_LED && type <= CORE_MODOUT_VFD_STROBE_1_16MS) + locals.outputStateBlock->controlledDeviceStates->states[lampNo].category = PINMAME_DEVICE_STATE_TYPE_LED; + else + locals.outputStateBlock->controlledDeviceStates->states[lampNo].category = PINMAME_DEVICE_STATE_TYPE_CUSTOM; + } + else + locals.outputStateBlock->controlledDeviceStates->states[lampNo].category = PINMAME_DEVICE_STATE_TYPE_CUSTOM; + } + locals.outputStateBlock->lampMatrixStates->stateByteSize = sizeof(pinmame_tDeviceState); + data += lampMatrixDeviceSize; + + // Alphanumeric segment displays + locals.outputStateBlock->alphaDisplayStates = (pinmame_tAlphaStates*)data; + pinmame_tAlphaSegmentState* alphaStates = (pinmame_tAlphaSegmentState*)((UINT8*)locals.outputStateBlock->alphaDisplayStates + sizeof(pinmame_tAlphaStates) + nSegDisplays * sizeof(pinmame_tAlphaDisplayDef)); + for (int i = 0; i < locals.nSortedSegLayout; i++) + { + alphaStates[i].type = locals.sortedSegLayout[i].type; + locals.outputStateBlock->alphaDisplayStates->displayDefs[locals.sortedSegLayout[i].top].nElements = locals.sortedSegLayout[i].length; + } + // TODO define more hardware hints + if (core_gameData->gen & GEN_GTS3) + for (int i = 0; i < nSegDisplays; i++) + locals.outputStateBlock->alphaDisplayStates->displayDefs[i].type = + locals.outputStateBlock->alphaDisplayStates->displayDefs[i].nElements == 20 ? PINMAME_DEVICE_TYPE_VFD_GREEN : PINMAME_DEVICE_TYPE_LED_RED; + else if (core_gameData->gen & (GEN_WPCALPHA_1 | GEN_WPCALPHA_2)) + for (int i = 0; i < nSegDisplays; i++) + locals.outputStateBlock->alphaDisplayStates->displayDefs[i].type = PINMAME_DEVICE_TYPE_NEON_PLASMA; + data += alphaDisplayDeviceSize; + + // Displays: this includes main DMD but also small LED matrix display (WOF, WPT, RBION,...) and video displays (Caveman, MrGame machines,...) + locals.outputStateBlock->displayStates = (pinmame_tDisplayStates*)data; + locals.outputStateBlock->displayStates->nDisplays = 0; + pinmame_tFrameState* nextDisplayFrame = (pinmame_tFrameState*)((UINT8*)locals.outputStateBlock->displayStates + sizeof(pinmame_tDisplayStates)); + for (layout = core_gameData->lcdLayout, parent_layout = NULL; layout->length || (parent_layout && parent_layout->length); layout += 1) { + if (layout->length == 0) { layout = parent_layout; parent_layout = NULL; } + switch (layout->type & CORE_SEGMASK) + { + case CORE_IMPORT: assert(parent_layout == NULL); parent_layout = layout + 1; layout = layout->lptr - 1; break; + case CORE_VIDEO: // Video display for games like Baby PacMan, frames are stored as RGB8 + case CORE_DMD: // DMD displays and LED matrices (for example RBION,... search for CORE_NODISP to list them) + locals.displayStateBlocks[locals.outputStateBlock->displayStates->nDisplays].layout = layout; + locals.displayStateBlocks[locals.outputStateBlock->displayStates->nDisplays].frame = nextDisplayFrame; + nextDisplayFrame->displayId = locals.outputStateBlock->displayStates->nDisplays; + nextDisplayFrame->width = layout->length; + nextDisplayFrame->height = layout->start; + if ((layout->type & CORE_SEGMASK) == CORE_VIDEO) { + nextDisplayFrame->dataFormat = PINMAME_FRAME_FORMAT_RGB; + nextDisplayFrame->structSize = sizeof(pinmame_tFrameState) + layout->length * layout->start * 3; + } + else { + nextDisplayFrame->dataFormat = PINMAME_FRAME_FORMAT_LUM; + nextDisplayFrame->structSize = sizeof(pinmame_tFrameState) + layout->length * layout->start; + } + nextDisplayFrame = (pinmame_tFrameState*)(((UINT8*)nextDisplayFrame) + nextDisplayFrame->structSize); + assert(((UINT8*)nextDisplayFrame - (UINT8*)locals.outputStateBlock->displayStates) <= displayStateSize); + locals.outputStateBlock->displayStates->nDisplays++; + break; + default: break; // Alphanumeric segment displays + } + } + data = nextDisplayFrame; + + // Raw DMD frames used for frame identification to perform colorization or drive PuP Packs + locals.outputStateBlock->rawDMDStates = (pinmame_tDisplayStates*)data; + locals.outputStateBlock->rawDMDStates->nDisplays = 0; + nextDisplayFrame = (pinmame_tFrameState*)((UINT8*)locals.outputStateBlock->rawDMDStates + sizeof(pinmame_tDisplayStates)); + int dspIndex = 0; + for (layout = core_gameData->lcdLayout, parent_layout = NULL; layout->length || (parent_layout && parent_layout->length); layout += 1) { + if (layout->length == 0) { layout = parent_layout; parent_layout = NULL; } + switch (layout->type & CORE_SEGMASK) + { + case CORE_IMPORT: assert(parent_layout == NULL); parent_layout = layout + 1; layout = layout->lptr - 1; break; + case CORE_VIDEO: dspIndex++; break; // Video display for games like Baby PacMan, frames are stored as RGB8 + case CORE_DMD: // DMD displays and LED matrices (for example RBION,... search for CORE_NODISP to list them) + locals.displayStateBlocks[dspIndex].raw = nextDisplayFrame; + nextDisplayFrame->displayId = dspIndex; + nextDisplayFrame->width = layout->length; + nextDisplayFrame->height = layout->start; + nextDisplayFrame->dataFormat = ((core_gameData->gen & (GEN_SAM | GEN_SPA | GEN_ALVG_DMD2)) || (strncasecmp(Machine->gamedrv->name, "smb", 3) == 0) || (strncasecmp(Machine->gamedrv->name, "cueball", 7) == 0)) ? PINMAME_FRAME_FORMAT_BP4 : PINMAME_FRAME_FORMAT_BP2; + nextDisplayFrame->structSize = sizeof(pinmame_tFrameState) + layout->length * layout->start; + nextDisplayFrame = (pinmame_tFrameState*)(((UINT8*)nextDisplayFrame) + nextDisplayFrame->structSize); + assert(((UINT8*)nextDisplayFrame - (UINT8*)locals.outputStateBlock->rawDMDStates) <= displayStateSize); + locals.outputStateBlock->rawDMDStates->nDisplays++; + dspIndex++; + break; + default: break; // Alphanumeric segment displays + } + } + data = nextDisplayFrame; +} + +pinmame_tMachineOutputState* core_getOutputState(const unsigned int updateMask) +{ + if (locals.outputStateBlock == NULL) + { + core_createStateBlock(); + if (locals.outputStateBlock == NULL) + return NULL; + } + + // HACK as timer_get_time is not thread safe and can only be called from emulation thread. + // We use a fake timer with timer_starttime which is thread safe as a workaround. + // const double now = timer_get_time(); + mame_timer fake_timer = { 0 }; + const double now = timer_starttime(&fake_timer); + + if (updateMask & PINMAME_STATE_REQMASK_GPOUTPUT_BINARY_STATE) + { + locals.outputStateBlock->controlledOutputBinaryStates->updateTimestamp = now; + locals.outputStateBlock->controlledOutputBinaryStates->outputBitset[0] = 0; + for (unsigned int i = 0; i < locals.outputStateBlock->controlledOutputBinaryStates->nOutputs; i++) + { + if ((locals.controlledOutputMapping[i].type == 1 || locals.controlledOutputMapping[i].type == 3) // Only for solenoids + && core_getPulsedSol(locals.controlledOutputMapping[i].solIndex)) // We need core_getPulsedSol to perform its mapping for WPC hardware + locals.outputStateBlock->controlledOutputBinaryStates->outputBitset[0] |= 1 << i; + } + } + + if (updateMask & PINMAME_STATE_REQMASK_GPOUTPUT_DEVICE_STATE) + { + core_update_pwm_solenoids(); + core_update_pwm_gis(); + locals.outputStateBlock->controlledDeviceStates->updateTimestamp = now; + for (unsigned int i = 0; i < locals.outputStateBlock->controlledDeviceStates->nDevices; i++) + { + switch (locals.controlledOutputMapping[i].type) + { + case 1: // Solenoid using core_getSol + locals.outputStateBlock->controlledDeviceStates->states[i].customState = core_getSol(locals.controlledOutputMapping[i].solIndex) ? 1 : 0; + break; + case 3: // GI using coreGlobals.gi + locals.outputStateBlock->controlledDeviceStates->states[i].customState = coreGlobals.gi[locals.controlledOutputMapping[i].solIndex]; + break; + case 5: // Solenoid using physic output + case 7: // GI using physic output + { + const int output = locals.controlledOutputMapping[i].physOutIndex; + const int type = coreGlobals.physicOutputState[output].type; + if (options.usemodsol & CORE_MODOUT_ENABLE_MODSOL) + locals.outputStateBlock->controlledDeviceStates->states[i].customState = saturatedByte(coreGlobals.physicOutputState[output].value); + else if (type >= CORE_MODOUT_BULB_44_6_3V_AC && type <= CORE_MODOUT_BULB_906_25V_DC_S11) + { + locals.outputStateBlock->controlledDeviceStates->states[i].bulb.luminance = coreGlobals.physicOutputState[output].value; + locals.outputStateBlock->controlledDeviceStates->states[i].bulb.filamentTemperature = 0.f; // Not yet implemented + } + else if (type >= CORE_MODOUT_LED && type <= CORE_MODOUT_VFD_STROBE_1_16MS) + locals.outputStateBlock->controlledDeviceStates->states[i].ledLuminance = coreGlobals.physicOutputState[output].value; + else + locals.outputStateBlock->controlledDeviceStates->states[i].customState = saturatedByte(coreGlobals.physicOutputState[output].value); + } + break; + } + } + } + + if (updateMask & PINMAME_STATE_REQMASK_LAMP_DEVICE_STATE) + { + core_update_pwm_lamps(); + locals.outputStateBlock->lampMatrixStates->updateTimestamp = now; + if (coreGlobals.nLamps && (options.usemodsol & (CORE_MODOUT_ENABLE_PHYSOUT_LAMPS))) + { + for (unsigned int i = 0; i < locals.outputStateBlock->lampMatrixStates->nDevices; i++) + { + const unsigned int lampNo = coreData->m2lamp ? coreData->m2lamp((i / 8) + 1, i & 7) : i; + const int type = coreGlobals.physicOutputState[CORE_MODOUT_LAMP0 + i].type; + if (type >= CORE_MODOUT_BULB_44_6_3V_AC && type <= CORE_MODOUT_BULB_906_25V_DC_S11) + locals.outputStateBlock->lampMatrixStates->states[lampNo].bulb.luminance = coreGlobals.physicOutputState[CORE_MODOUT_LAMP0 + i].value; + else if (type >= CORE_MODOUT_LED && type <= CORE_MODOUT_VFD_STROBE_1_16MS) + locals.outputStateBlock->lampMatrixStates->states[lampNo].ledLuminance = coreGlobals.physicOutputState[CORE_MODOUT_LAMP0 + i].value; + else + locals.outputStateBlock->lampMatrixStates->states[lampNo].customState = saturatedByte(coreGlobals.physicOutputState[CORE_MODOUT_LAMP0 + i].value); + } + } + else + { + const int hasSAMModulatedLeds = (core_gameData->gen & GEN_SAM) && (core_gameData->hw.lampCol > 2); + const unsigned int nCol = CORE_STDLAMPCOLS + (hasSAMModulatedLeds ? 2 : core_gameData->hw.lampCol); + for (unsigned int col = 0; col < nCol; col++) + for (unsigned int row = 0, rowLamp = coreGlobals.lampMatrix[col]; row < 8; row++, rowLamp >>= 1) + { + const unsigned int lampNo = coreData->m2lamp ? coreData->m2lamp(col + 1, row) : (col * 8 + row); + locals.outputStateBlock->lampMatrixStates->states[lampNo].customState = rowLamp & 0x01; + } + // Backward compatibility for modulated LED & RGB LEDs of SAM hardware + if (hasSAMModulatedLeds) + for (unsigned int i = 80; i < (unsigned int)coreGlobals.nLamps; i++) + locals.outputStateBlock->lampMatrixStates->states[i].customState = saturatedByte(coreGlobals.physicOutputState[CORE_MODOUT_LAMP0 + i].value); + } + } + + if (updateMask & PINMAME_STATE_REQMASK_ALPHA_DEVICE_STATE) + { + core_update_pwm_segments(); + pinmame_tAlphaStates* alphaDisplay = locals.outputStateBlock->alphaDisplayStates; + alphaDisplay->updateTimestamp = now; + static int nSegments[] = {7, 8, 8, 8, 9, 10, 10, 14, 15, 16, 16, 16, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int segDisplayStart = 0; + pinmame_tAlphaSegmentState* alphaStates = (pinmame_tAlphaSegmentState*)((UINT8*)alphaDisplay + sizeof(pinmame_tAlphaStates) + alphaDisplay->nDisplays * sizeof(pinmame_tAlphaDisplayDef)); + for (int i = 0; i < locals.nSortedSegLayout; i++) + { + const core_tLCDLayout layout = locals.sortedSegLayout[i]; + const int type = layout.type & CORE_SEGALL; + const int nSegs = nSegments[type]; + if (coreGlobals.nAlphaSegs && (options.usemodsol & (CORE_MODOUT_FORCE_ON | CORE_MODOUT_ENABLE_PHYSOUT_ALPHASEGS))) + { + int pos = CORE_MODOUT_SEG0 + layout.start * 16; + if (layout.type & CORE_SEGHIBIT) pos += 8; + for (int j = 0; j < nSegs; j++) // Loop over each segments of the current character (up to 16) + alphaStates[i].luminance[j] = coreGlobals.physicOutputState[pos + j].value; + if ((type == CORE_SEG98F) || (type == CORE_SEG87F)) + { + // Forced comma if at least one segment is on + float max = 0.f; + for (int j = 0; j < nSegs; j++) + if (j != 8 && alphaStates[i].luminance[j] > max) + max = alphaStates[i].luminance[j]; + alphaStates[i].luminance[8] = max; + } + } + else + { + UINT16* seg = &coreGlobals.segments[layout.start].w; + UINT16 tmpSeg = *seg; + if (layout.type & CORE_SEGHIBIT) tmpSeg >>= 8; + // if (type == CORE_SEG9) tmpSeg |= (tmpSeg & 0x100) << 1; // This is a consequence of pinmame rendering, not the segment layout + else if (tmpSeg && ((type == CORE_SEG98F) || (type == CORE_SEG87F))) tmpSeg |= 0x80; // Forced comma if at least one segment is on + for (int j = 0; j < nSegs; j++, tmpSeg >>= 1) // Loop over each segments of the current character (up to 16) + alphaStates[i].luminance[j] = (tmpSeg & 1) ? 1.f : 0.f; + } + } + } + + if (updateMask & PINMAME_STATE_REQMASK_DISPLAY_STATE) + { + //const struct rectangle* cliprect; + for (unsigned int i = 0; i < locals.outputStateBlock->displayStates->nDisplays; i++) + { + struct core_dispLayout* layout = locals.displayStateBlocks[i].layout; + if (layout->fptr && (layout->type & CORE_SEGALL) == CORE_DMD) + { + // Nothing to do for DMD and LED matrix as they are directly updated in core_dmd_video_update (to get the raw data instead of rendered one, needed for correct shading and coloring plugins) + // TODO we should perform DMD update here, on request, to get less animation stuterring (for the time being, we update at 60Hz while the emulated DMD has a higher refresh rate and the user + // display wan have any display rate, so we end up creating stutters), but this design would need DMD update to be thread safe as core_getOutputState can be called from another thread than + // the emulation thread. + //if (((ptPinMAMEvidUpdate)(layout->fptr))(Machine->scrbitmap, &cliprect, layout) == 0) + { + } + } + else if (layout->fptr && (layout->type & CORE_SEGALL) == CORE_VIDEO) + { + // FIXME implement + //if (((ptPinMAMEvidUpdate)(layout->fptr))(Machine->scrbitmap, cliprect, layout) == 0) + { + pinmame_tFrameState* frame = locals.displayStateBlocks[i].frame; + frame->updateTimestamp = now; + } + } + } + } + + return locals.outputStateBlock; +} + /*-------------------- / Draw a LED digit /---------------------*/ @@ -2191,6 +2747,9 @@ static MACHINE_INIT(core) { // DMD USB Init if(g_fShowPinDMD && !time_to_reset) dmddeviceInit(g_szGameName, core_gameData->gen, &pmoptions); + + locals.outputStateBlock = NULL; + locals.outputStateSharedMem = INVALID_HANDLE_VALUE; #endif /*-- Generate LUTs for VPinMAME DMD --*/ @@ -2258,7 +2817,22 @@ static MACHINE_STOP(core) { #ifdef VPINMAME // DMD USB Kill if(g_fShowPinDMD && !time_to_reset) - dmddeviceDeInit(); + dmddeviceDeInit(); + + if (locals.outputStateSharedMem != INVALID_HANDLE_VALUE) + { + UINT8* sharedMem = (UINT8*)(locals.outputStateBlock) - sizeof(unsigned int); + UnmapViewOfFile(sharedMem); + CloseHandle(locals.outputStateSharedMem); + locals.outputStateSharedMem = INVALID_HANDLE_VALUE; + locals.outputStateBlock = NULL; + } +#else + if (locals.outputStateBlock) + { + free(locals.outputStateBlock); + locals.outputStateBlock = NULL; + } #endif #if defined(VPINMAME) || defined(LIBPINMAME) g_raw_dmdx = ~0u; @@ -3381,6 +3955,32 @@ void core_dmd_render_dmddevice(const int width, const int height, const UINT8* c } #endif +// Prepare data for VPinMame state block +#if defined(VPINMAME) || defined(LIBPINMAME) +void core_dmd_update_state_block(const struct core_dispLayout* layout, const UINT8* dmdDotRaw, const UINT8* dmdDotLum) { + if (locals.outputStateBlock && locals.outputStateBlock->displayStates) { + for (unsigned int i = 0; i < locals.outputStateBlock->displayStates->nDisplays; i++) { + if (locals.displayStateBlocks[i].layout == layout) { + const unsigned int size = layout->start * layout->length; + locals.displayStateBlocks[i].frame->updateTimestamp = timer_get_time(); + if (memcmp(locals.displayStateBlocks[i].frame->frameData, dmdDotLum, size) != 0) + { + memcpy(locals.displayStateBlocks[i].frame->frameData, dmdDotLum, size); + locals.displayStateBlocks[i].frame->frameId++; + } + locals.displayStateBlocks[i].raw->updateTimestamp = timer_get_time(); + if (memcmp(locals.displayStateBlocks[i].raw->frameData, dmdDotRaw, size) != 0) + { + memcpy(locals.displayStateBlocks[i].raw->frameData, dmdDotRaw, size); + locals.displayStateBlocks[i].raw->frameId++; + } + return; + } + } + } +} +#endif + // Save main DMD bitplane and raw frames to a capture file // DMD frame capture can be enabled either by: // - setting g_fDumpFrames (not supported as it is only available through keyboard input which VPinMame doesn't have) @@ -3483,7 +4083,7 @@ void core_dmd_video_update(struct mame_bitmap *bitmap, const struct rectangle *c core_dmd_render_lpm(layout->length, layout->start, dmdDotLum, dmdDotRaw); has_DMD_Video = 1; } - + core_dmd_update_state_block(layout, dmdDotRaw, dmdDotLum); #elif defined(VPINMAME) const int isMainDMD = layout->length >= 128; // Up to 2 main DMDs (1 for all games, except Strikes N' Spares which has 2) // FIXME check for VPinMame window hidden/shown state, and do not render if hidden @@ -3494,10 +4094,58 @@ void core_dmd_video_update(struct mame_bitmap *bitmap, const struct rectangle *c core_dmd_render_dmddevice(layout->length, layout->start, dmdDotLum, dmdDotRaw, layout->top != 0); core_dmd_capture_frame(layout->length, layout->start, dmdDotRaw, raw_dmd_frame_count ,raw_dmd_frames); } - + core_dmd_update_state_block(layout, dmdDotRaw, dmdDotLum); #elif defined(PINMAME) core_dmd_render_internal(bitmap, layout->left, layout->top, layout->length, layout->start, dmdDotLum, pmoptions.dmd_antialias && !(layout->type & CORE_DMDNOAA)); + #endif +} +void core_display_video_update(struct mame_bitmap* bitmap, const struct rectangle* cliprect, const struct core_dispLayout* layout, const int rotation) { + #if defined(VPINMAME) + if (locals.outputStateBlock && locals.outputStateBlock->displayStates) { + for (unsigned int i = 0; i < locals.outputStateBlock->displayStates->nDisplays; i++) { + if (locals.displayStateBlocks[i].layout == layout) { + const unsigned int size = layout->start * layout->length; + locals.displayStateBlocks[i].frame->updateTimestamp = timer_get_time(); + locals.displayStateBlocks[i].frame->frameId++; + for (int y = 0; y < layout->start; y++) { + for (int x = 0; x < layout->length; x++) { + pen_t col = bitmap->read(bitmap, x, y); + UINT8 r, g, b; + palette_get_color(col, &r, &g, &b); + switch (rotation) + { + case 0: + locals.displayStateBlocks[i].frame->width = layout->length; + locals.displayStateBlocks[i].frame->height = layout->start; + locals.displayStateBlocks[i].frame->frameData[(x + y * layout->length) * 3 ] = r; + locals.displayStateBlocks[i].frame->frameData[(x + y * layout->length) * 3 + 1] = g; + locals.displayStateBlocks[i].frame->frameData[(x + y * layout->length) * 3 + 2] = b; + break; + case 1: + locals.displayStateBlocks[i].frame->width = layout->start; + locals.displayStateBlocks[i].frame->height = layout->length; + locals.displayStateBlocks[i].frame->frameData[((layout->start - 1 - y) + x * layout->start) * 3 ] = r; + locals.displayStateBlocks[i].frame->frameData[((layout->start - 1 - y) + x * layout->start) * 3 + 1] = g; + locals.displayStateBlocks[i].frame->frameData[((layout->start - 1 - y) + x * layout->start) * 3 + 2] = b; + break; + case 3: + locals.displayStateBlocks[i].frame->width = layout->start; + locals.displayStateBlocks[i].frame->height = layout->length; + locals.displayStateBlocks[i].frame->frameData[(y + (layout->length - 1 - x) * layout->start) * 3] = r; + locals.displayStateBlocks[i].frame->frameData[(y + (layout->length - 1 - x) * layout->start) * 3 + 1] = g; + locals.displayStateBlocks[i].frame->frameData[(y + (layout->length - 1 - x) * layout->start) * 3 + 2] = b; + break; + default: + assert(FALSE); + break; + } + } + } + break; + } + } + } #endif } diff --git a/src/wpc/core.h b/src/wpc/core.h index 401134264..881427655 100644 --- a/src/wpc/core.h +++ b/src/wpc/core.h @@ -10,6 +10,10 @@ #include "gen.h" #include "sim.h" +#include "drawgfx.h" + +#include "pinmamedef.h" + /*-- some convenience macros --*/ #ifndef FALSE #define FALSE (0) @@ -229,7 +233,7 @@ struct core_dispLayout { typedef struct core_dispLayout core_tLCDLayout, *core_ptLCDLayout; // Overall alphanumeric display layout. Used externally by VPinMame's dmddevice. Don't change order typedef enum { - CORE_SEGLAYOUT_None, + CORE_SEGLAYOUT_None = 0, CORE_SEGLAYOUT_2x16Alpha, CORE_SEGLAYOUT_2x20Alpha, CORE_SEGLAYOUT_2x7Alpha_2x7Num, @@ -247,7 +251,7 @@ typedef enum { CORE_SEGLAYOUT_1x7Num_1x16Alpha_1x16Num, CORE_SEGLAYOUT_1x16Alpha_1x16Num_1x7Num_1x4Num, CORE_SEGLAYOUT_Invalid -} core_segOverallLayout_t; +} core_tSegOverallLayout; #define PINMAME_VIDEO_UPDATE(name) int (name)(struct mame_bitmap *bitmap, const struct rectangle *cliprect, const struct core_dispLayout *layout) @@ -607,6 +611,9 @@ extern int core_getPulsedSol(int solNo); extern UINT64 core_getAllSol(void); extern void core_getAllPhysicSols(float* const state); +/*-- full output state --*/ +extern pinmame_tMachineOutputState* core_getOutputState(const unsigned int updateMask); + /*-- AC sync and PWM integration --*/ extern void core_update_pwm_outputs(const int startIndex, const int count); INLINE void core_update_pwm_gis(void) { if (options.usemodsol & (CORE_MODOUT_FORCE_ON | CORE_MODOUT_ENABLE_PHYSOUT_GI)) core_update_pwm_outputs(CORE_MODOUT_GI0, coreGlobals.nGI); } @@ -669,6 +676,8 @@ extern void core_dmd_submit_frame(core_tDMDPWMState* dmd_state, const UINT8* fra extern void core_dmd_update_pwm(core_tDMDPWMState* dmd_state); extern void core_dmd_video_update(struct mame_bitmap *bitmap, const struct rectangle *cliprect, const struct core_dispLayout *layout, core_tDMDPWMState* dmd_state); +extern void core_display_video_update(struct mame_bitmap* bitmap, const struct rectangle* cliprect, const struct core_dispLayout* layout, const int rotation); + extern void core_sound_throttle_adj(int sIn, int *sOut, int buffersize, double samplerate); /*-- nvram handling --*/ diff --git a/src/wpc/pinmamedef.h b/src/wpc/pinmamedef.h new file mode 100644 index 000000000..9af9a76b1 --- /dev/null +++ b/src/wpc/pinmamedef.h @@ -0,0 +1,169 @@ +#pragma once + +// This file contains definitions used inside PinMame and for external integration through libpinmame + + +/////////////////////////////////////////////////////////////////////////////// +// +// Complete machine output state, provided in a global state block +// +// 0 length array is a non standard extension used intentionally here. +// Corresponding warning #4200 is therefore disabled when needed. +// + +// Digital output instantaneous states + +#pragma warning(disable : 4200) +typedef struct +{ + double updateTimestamp; + unsigned int nOutputs; + UINT32 outputBitset[]; // Bitset array of nOutputs bits with their current binary state +} pinmame_tBinaryStates; + + +// Controlled device states + +typedef enum { + PINMAME_DEVICE_STATE_TYPE_CUSTOM, // Custom state defined by each driver (value maybe either binary of 0/1 or 0/255, or modulated between 0..255) + PINMAME_DEVICE_STATE_TYPE_BULB, // Bulb state defined by its relative luminance and average filament temperature + PINMAME_DEVICE_STATE_TYPE_LED, // LED state defined by its relative luminance + PINMAME_DEVICE_STATE_TYPE_SOLENOID, // Solenoid (not yet defined) + PINMAME_DEVICE_STATE_TYPE_STEPPER, // Stepper motor (not yet defined) +} pinmame_tDeviceCategory; + +typedef enum { + PINMAME_DEVICE_TYPE_UNDEFINED, // Hardware type identifier + PINMAME_DEVICE_TYPE_VFD_BLUE, // VFD used for segment display + PINMAME_DEVICE_TYPE_VFD_GREEN, // VFD used for segment display + PINMAME_DEVICE_TYPE_NEON_PLASMA, // Neon Plasma display (Panaplex, Burroughs,...) used for segment and dot matrix display + PINMAME_DEVICE_TYPE_LED_RED, // +} pinmame_tDeviceType; + +typedef struct +{ + pinmame_tDeviceCategory category; + pinmame_tDeviceType type; + union + { + // PINMAME_DEVICE_STATE_TYPE_CUSTOM + UINT8 customState; // Custom value, depending on each driver definition + + // PINMAME_DEVICE_STATE_TYPE_BULB + struct + { + float luminance; // relative luminance to bulb rating (equals 1.f when bulb is under its rating voltage after heating stabilization) + float filamentTemperature; // perceived filament temperature (equals to bulb filament rating when bulb is at its rating voltage after heating stabilization) + } bulb; + + // PINMAME_DEVICE_STATE_TYPE_LED + float ledLuminance; // relative luminance to bulb design (equals 1.f when LED is pulsed at its designed PWM) + + // PINMAME_DEVICE_STATE_TYPE_SOLENOID + // To be added in a later revision + + // PINMAME_DEVICE_STATE_TYPE_STEPPER + // To be added in a later revision + }; +} pinmame_tDeviceState; + +#pragma warning(disable : 4200) +typedef struct +{ + double updateTimestamp; + unsigned int nDevices; + unsigned int stateByteSize; // sizeof(pinmame_tDeviceState), to be used to access states since the size of a single state my avolve in future revisions + pinmame_tDeviceState states[]; // array of nDevices * stateByteSize with the current device states +} pinmame_tDeviceStates; + + +// Alphanumeric segment displays + +// Individual segment layouts inside a display (usually made of multiple elements) +typedef enum { + PINMAME_SEG_LAYOUT_7, // 7 segments + PINMAME_SEG_LAYOUT_7F, // 7 segments and comma, comma is lit if at least one segment is on + PINMAME_SEG_LAYOUT_8, // 7 segments and comma + PINMAME_SEG_LAYOUT_8D, // 7 segments and dot + PINMAME_SEG_LAYOUT_9, // 9 segments + PINMAME_SEG_LAYOUT_9F, // 9 segments and comma, comma is lit if at least one segment is on + PINMAME_SEG_LAYOUT_10, // 9 segments and comma + PINMAME_SEG_LAYOUT_14, // 14 segments (correspond to CORE_SEG16N) + PINMAME_SEG_LAYOUT_15, // 14 segments with dot (correspond to CORE_SEG16D) + PINMAME_SEG_LAYOUT_16, // 14 segments with dot and comma (correspond to CORE_SEG16) + PINMAME_SEG_LAYOUT_16R, // 14 segments with swapped dot and comma (correspond to CORE_SEG16R) + PINMAME_SEG_LAYOUT_16S, // 16 segments (correspond to CORE_SEG16S) +} pinmame_tSegElementType; + +typedef struct +{ + pinmame_tSegElementType type; // see PINMAME_SEG_LAYOUT_xxx + float luminance[16]; // relative luminance of each segment (from 7 to 16) +} pinmame_tAlphaSegmentState; + +typedef struct +{ + unsigned int nElements; // Number of elements (each element is composed of 7 to 16 segments) + pinmame_tDeviceType type; // Hardware reference (Panaplex, VFD, ...) +} pinmame_tAlphaDisplayDef; + +#pragma warning(disable : 4200) +typedef struct +{ + double updateTimestamp; + unsigned int nDisplays; // Number of displays (each display is composed of multiple elements) + pinmame_tAlphaDisplayDef displayDefs[]; // Definition of each display (number of elements, hardware type,...) + // pinmame_tAlphaSegmentState states[]; // State of each display element as a linear array +} pinmame_tAlphaStates; + + +// DMD and video displays + +typedef enum { + PINMAME_FRAME_FORMAT_LUM, // Linear luminance (for monochrome DMD) + PINMAME_FRAME_FORMAT_RGB, // sRGB (for video frame) + PINMAME_FRAME_FORMAT_BP2, // 2 bitplanes, only used to identify frames + PINMAME_FRAME_FORMAT_BP4 // 4 bitplanes, only used to identify frames +} pinmame_tFrameDataFormat; + +#pragma warning(disable : 4200) +typedef struct +{ + unsigned int structSize; // Struct size including header and frame data in bytes (for safe DMD/Display array iteration) + unsigned int displayId; // Unique Id, shared between render frame and raw frame used for frame identification + double updateTimestamp; + unsigned int width; + unsigned int height; + pinmame_tFrameDataFormat dataFormat; + unsigned int frameId; + UINT8 frameData[]; // The display frame data which size depends on width, height and data format +} pinmame_tFrameState; + +typedef struct +{ + unsigned int nDisplays; + // pinmame_tFrameState displays[]; // Array of nDisplays * pinmame_tFrameState (can't be directly declared since frame size is undefined) +} pinmame_tDisplayStates; + + +// Global output state block + +typedef struct +{ + unsigned int versionID; // Data block format version (current version + pinmame_tBinaryStates* controlledOutputBinaryStates; // Binary state (instantaneous state of controlled output) + pinmame_tDeviceStates* controlledDeviceStates; // Device state (state of the emulated device) + pinmame_tDeviceStates* lampMatrixStates; // State of emulated lamps powered from a strobed lamp matrix + pinmame_tAlphaStates* alphaDisplayStates; // State of alphanumeric segment displays + pinmame_tDisplayStates* displayStates; // State of displays and DMDs + pinmame_tDisplayStates* rawDMDStates; // Raw logic state of DMDs (stable view of the stae that can be used for frame identification) +} pinmame_tMachineOutputState; + +#define PINMAME_STATE_REQMASK_GPOUTPUT_BINARY_STATE 0x01 +#define PINMAME_STATE_REQMASK_GPOUTPUT_DEVICE_STATE 0x02 +#define PINMAME_STATE_REQMASK_LAMP_DEVICE_STATE 0x04 +#define PINMAME_STATE_REQMASK_ALPHA_DEVICE_STATE 0x08 +#define PINMAME_STATE_REQMASK_DISPLAY_STATE 0x10 +#define PINMAME_STATE_REQMASK_RAW_DMD_STATE 0x20 +#define PINMAME_STATE_REQMASK_ALL 0x3F + diff --git a/src/wpc/sam.c b/src/wpc/sam.c index 4c0f21ac6..d037678fb 100644 --- a/src/wpc/sam.c +++ b/src/wpc/sam.c @@ -2252,7 +2252,9 @@ static PINMAME_VIDEO_UPDATE(samminidmd_update) { bits = (bits << 1) | (coreGlobals.dmdDotRaw[y * 5 + x] ? 1 : 0); coreGlobals.drawSeg[35 * dmd_y + 5 * dmd_x + x] = bits; } +#ifndef VPINMAME if (!pmoptions.dmd_only) +#endif core_dmd_video_update(bitmap, cliprect, layout, NULL); return 0; } @@ -2274,8 +2276,10 @@ static PINMAME_VIDEO_UPDATE(samminidmd2_update) { bits = (bits<<1) | (coreGlobals.dmdDotRaw[kk * layout->length + ii] ? 1 : 0); coreGlobals.drawSeg[ii] = bits; } - if (!pmoptions.dmd_only) - core_dmd_video_update(bitmap, cliprect, layout, NULL); +#ifndef VPINMAME + if (!pmoptions.dmd_only) +#endif + core_dmd_video_update(bitmap, cliprect, layout, NULL); return 0; }