diff --git a/cmake/libpinmame/CMakeLists.txt b/cmake/libpinmame/CMakeLists.txt index e2abb1ddf..642cecc3b 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/libpinmame/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..b67d1f717 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/libpinmame/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..2a50bc80b 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/libpinmame/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..963dd0a19 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/libpinmame/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..cf14a13cc 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/libpinmame/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..5e5e6a9d3 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/libpinmame/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..87806e0b9 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/libpinmame/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..32f1c610d 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/libpinmame/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..ccaf1257c 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/libpinmame/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..30fa852e8 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 "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/pinmamedef.h b/src/libpinmame/pinmamedef.h new file mode 100644 index 000000000..c4ff80ec4 --- /dev/null +++ b/src/libpinmame/pinmamedef.h @@ -0,0 +1,170 @@ +#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; + unsigned int mapping; // Device mapping to a user understandable id (for example lamp matrix are usually numbered by row/column and not index) + 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_7C, // 7 segments with comma + PINMAME_SEG_LAYOUT_7D, // 7 segments with dot + PINMAME_SEG_LAYOUT_9, // 9 segments + PINMAME_SEG_LAYOUT_9C, // 9 segments with comma + PINMAME_SEG_LAYOUT_14, // 14 segments + PINMAME_SEG_LAYOUT_14D, // 14 segments with dot + PINMAME_SEG_LAYOUT_14DC, // 14 segments with dot and comma + PINMAME_SEG_LAYOUT_16, // 16 segments +} 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; +#define PINMAME_STATE_BLOCK_FIRST_ALPHA_FRAME(pAlphaState) ((pinmame_tAlphaSegmentState*)((UINT8*)(pAlphaState) + sizeof(pinmame_tAlphaStates) + (pAlphaState->nDisplays) * sizeof(pinmame_tAlphaDisplayDef))) + + +// 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; +#define PINMAME_STATE_BLOCK_FIRST_DISPLAY_FRAME(pDisplayState) ((pinmame_tFrameState*)((UINT8*)(pDisplayState) + sizeof(pinmame_tDisplayStates))) +#define PINMAME_STATE_BLOCK_NEXT_DISPLAY_FRAME(pFrameState) ((pinmame_tFrameState*)((UINT8*)(pFrameState) + (pFrameState)->structSize)) + + +// 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/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..834349ebf 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) { @@ -681,7 +684,7 @@ static const tSegData segData[2][18] = {{ {20,15,&segSize1C[2][0]},/* SEG10 */ {20,15,&segSize1[2][0]}, /* SEG9 */ {20,15,&segSize1C[1][0]},/* SEG8 */ - {20,15,&segSize1C[4][0]},/* SEG8FD */ + {20,15,&segSize1C[4][0]},/* SEG8D */ {20,15,&segSize1[1][0]}, /* SEG7 */ {20,15,&segSize1C[1][0]},/* SEG87 */ {20,15,&segSize1C[1][0]},/* SEG87F */ @@ -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,552 @@ 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 digital state (limited to the first 32 ones since the emulation core does not store the others) + const unsigned int nControlledOutput = MIN(32, coreGlobals.nGI + (coreGlobals.nSolenoids ? coreGlobals.nSolenoids : (CORE_FIRSTCUSTSOL - 1 + core_gameData->hw.custSol))); + const unsigned int controlledOutputBinarySize = sizeof(pinmame_tBinaryStates) + ((nControlledOutput + 31) >> 5) * 4; + // Controlled device + const unsigned int nControlledDevice = coreGlobals.nGI + (coreGlobals.nSolenoids ? coreGlobals.nSolenoids : (CORE_FIRSTCUSTSOL - 1 + core_gameData->hw.custSol)); + const unsigned int controlledOutputDeviceSize = sizeof(pinmame_tDeviceStates) + nControlledDevice * sizeof(pinmame_tDeviceState); + // Lamps + 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)); + const unsigned int lampMatrixDeviceSize = sizeof(pinmame_tDeviceStates) + nLamps * sizeof(pinmame_tDeviceState); + // DMD, video, 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; + displayStateSize += sizeof(pinmame_tFrameState) + layout->length * layout->start * 1; + break; + 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 or WPC). + // The CORE_SEG mask also mix segment layouts (number of segment, with/without dot & comma, ...) with segment addressing (which output + // drive the segment, is the segment driuven together with another segment, ...). The CORE_SEG mask can also include a comma every + // three digit information, and the CORE_SEGREV flag which indicates that the memory position is in reversed order. + // We resolve all these to simply expose physical layouts, with stable output order. To do so, we convert them to individual + // elements and group them based on their declaration order and render position, then process the additional flags at setup here, + // or when accessing data (for example, to process shared segment command). + 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; + int isThousands = (j > 0) && (j % 3 == 0); + 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_7; break; + case CORE_SEG87F: + type = isThousands ? PINMAME_SEG_LAYOUT_7C : PINMAME_SEG_LAYOUT_7; + if (!isThousands) + locals.sortedSegLayout[locals.nSortedSegLayout].type = CORE_SEG87 | (locals.sortedSegLayout[locals.nSortedSegLayout].type & ~CORE_SEGALL); + break; + case CORE_SEG87: type = isThousands ? PINMAME_SEG_LAYOUT_7C : PINMAME_SEG_LAYOUT_7; break; + case CORE_SEG8: type = PINMAME_SEG_LAYOUT_7C; break; + case CORE_SEG8D: type = PINMAME_SEG_LAYOUT_7D; break; + case CORE_SEG9: type = PINMAME_SEG_LAYOUT_9; break; + case CORE_SEG10: type = PINMAME_SEG_LAYOUT_9C; break; + case CORE_SEG98F: + type = isThousands ? PINMAME_SEG_LAYOUT_9C : PINMAME_SEG_LAYOUT_9; + if (!isThousands) + locals.sortedSegLayout[locals.nSortedSegLayout].type = CORE_SEG98 | (locals.sortedSegLayout[locals.nSortedSegLayout].type & ~CORE_SEGALL); + break; + case CORE_SEG98: type = isThousands ? PINMAME_SEG_LAYOUT_9C : PINMAME_SEG_LAYOUT_9; break; // (8 segment is 9 with single command for vertical center) + case CORE_SEG16N: type = PINMAME_SEG_LAYOUT_14; break; + case CORE_SEG16D: type = PINMAME_SEG_LAYOUT_14D; break; + case CORE_SEG16: type = PINMAME_SEG_LAYOUT_14DC; break; + case CORE_SEG16R: type = PINMAME_SEG_LAYOUT_14DC; break; + case CORE_SEG16S: type = PINMAME_SEG_LAYOUT_16; 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)) // There is gap before next element + // 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); + // Final machine output state size + 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. The version is a data format identifier that clients must check before reading anything. + // It should be changed if and only if data structure changes prevents existing implementations to read it, therefore breaking backward compatibility + locals.outputStateBlock->versionID = 1; + data += headerSize; + + // Controlled outputs: all digital general purpose controlled outputs (usually wired to physical devices like solenoids, but also flashers, motors, child boards, GI,...) with a fairly messy mapping + locals.outputStateBlock->controlledOutputBinaryStates = (pinmame_tBinaryStates*)data; + locals.outputStateBlock->controlledOutputBinaryStates->nOutputs = nControlledOutput; + data += controlledOutputBinarySize; + + // Controlled devices: we directly map one digital output to one device (which is sometime wrong) + locals.outputStateBlock->controlledDeviceStates = (pinmame_tDeviceStates*)data; + locals.outputStateBlock->controlledDeviceStates->nDevices = nControlledDevice; + 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; + // FIXME we should take the opportunity to tidy up the overall mapping mess + // For example we could split per board outputs to avoid overlays which have lead to the existing map (i.e. splitting solenoids, CPU controlled flipper board, WPC GI, extension boards, ...) + locals.outputStateBlock->controlledDeviceStates->states[i].mapping = i; + 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 = nLamps; + locals.outputStateBlock->lampMatrixStates->stateByteSize = sizeof(pinmame_tDeviceState); + for (int i = 0; i < nLamps; i++) + { + locals.outputStateBlock->lampMatrixStates->states[i].mapping = coreData->m2lamp ? coreData->m2lamp((i / 8) + 1, i & 7) : i; + 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->lampMatrixStates->states[i].category = PINMAME_DEVICE_STATE_TYPE_BULB; + else if (type >= CORE_MODOUT_LED && type <= CORE_MODOUT_VFD_STROBE_1_16MS) + locals.outputStateBlock->lampMatrixStates->states[i].category = PINMAME_DEVICE_STATE_TYPE_LED; + else + locals.outputStateBlock->lampMatrixStates->states[i].category = PINMAME_DEVICE_STATE_TYPE_CUSTOM; + } + else + locals.outputStateBlock->lampMatrixStates->states[i].category = PINMAME_DEVICE_STATE_TYPE_CUSTOM; + } + data += lampMatrixDeviceSize; + + // Alphanumeric segment displays + locals.outputStateBlock->alphaDisplayStates = (pinmame_tAlphaStates*)data; + locals.outputStateBlock->alphaDisplayStates->nDisplays = nSegDisplays; + pinmame_tAlphaSegmentState* alphaStates = PINMAME_STATE_BLOCK_FIRST_ALPHA_FRAME(locals.outputStateBlock->alphaDisplayStates); + 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_STATE_BLOCK_FIRST_DISPLAY_FRAME(locals.outputStateBlock->displayStates); + 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_STATE_BLOCK_NEXT_DISPLAY_FRAME(nextDisplayFrame); + assert(((UINT8*)nextDisplayFrame - (UINT8*)locals.outputStateBlock->displayStates) <= displayStateSize); + locals.outputStateBlock->displayStates->nDisplays++; + break; + default: break; // Alphanumeric segment displays + } + } + data = (UINT8*)nextDisplayFrame; + + // Raw DMD frames used for frame identification to perform colorization or identify game events + locals.outputStateBlock->rawDMDStates = (pinmame_tDisplayStates*)data; + locals.outputStateBlock->rawDMDStates->nDisplays = 0; + nextDisplayFrame = PINMAME_STATE_BLOCK_FIRST_DISPLAY_FRAME(locals.outputStateBlock->rawDMDStates); + 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: // We do not provide raw frame for video output but we match displayId between raw and render frame + dspIndex++; + break; + case CORE_DMD: // Raw frame for DMD displays is a combination of the last PWM binary frames + 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_STATE_BLOCK_NEXT_DISPLAY_FRAME(nextDisplayFrame); + assert(((UINT8*)nextDisplayFrame - (UINT8*)locals.outputStateBlock->rawDMDStates) <= displayStateSize); + locals.outputStateBlock->rawDMDStates->nDisplays++; + dspIndex++; + break; + default: break; // Alphanumeric segment displays + } + } + data = (UINT8*)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; + } + default: // Invalid mapping + assert(0); + break; + } + } + } + + if (updateMask & PINMAME_STATE_REQMASK_LAMP_DEVICE_STATE) + { + core_update_pwm_lamps(); + pinmame_tDeviceStates* lampMatrix = locals.outputStateBlock->lampMatrixStates; + lampMatrix->updateTimestamp = now; + if (coreGlobals.nLamps && (options.usemodsol & (CORE_MODOUT_ENABLE_PHYSOUT_LAMPS))) + { + for (unsigned int i = 0; i < lampMatrix->nDevices; i++) + { + switch (lampMatrix->states[i].category) + { + case PINMAME_DEVICE_STATE_TYPE_BULB: lampMatrix->states[i].bulb.luminance = coreGlobals.physicOutputState[CORE_MODOUT_LAMP0 + i].value; break; + case PINMAME_DEVICE_STATE_TYPE_LED: lampMatrix->states[i].ledLuminance = coreGlobals.physicOutputState[CORE_MODOUT_LAMP0 + i].value; break; + case PINMAME_DEVICE_STATE_TYPE_CUSTOM: lampMatrix->states[i].customState = saturatedByte(coreGlobals.physicOutputState[CORE_MODOUT_LAMP0 + i].value); break; + } + } + } + else + { + // Backward compatibility for modulated LED & RGB LEDs of SAM hardware: 2 regular columns, then modulated LEDs + 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) + lampMatrix->states[8 * col + row].customState = rowLamp & 0x01; + if (hasSAMModulatedLeds) + for (unsigned int i = 80; i < (unsigned int)coreGlobals.nLamps; i++) + lampMatrix->states[i].customState = saturatedByte(coreGlobals.physicOutputState[CORE_MODOUT_LAMP0 + i].value); + } + } + + if (updateMask & PINMAME_STATE_REQMASK_ALPHA_DEVICE_STATE) + { + if (coreGlobals.nAlphaSegs) // Force update as we always return PWM value if available + core_update_pwm_outputs(CORE_MODOUT_SEG0, coreGlobals.nAlphaSegs); + pinmame_tAlphaStates* alphaDisplay = locals.outputStateBlock->alphaDisplayStates; + alphaDisplay->updateTimestamp = now; + static int nSegments[] = { 7, 8, 8, 9, 10, 14, 15, 16, 16 }; + int segDisplayStart = 0; + pinmame_tAlphaSegmentState* alphaStates = PINMAME_STATE_BLOCK_FIRST_ALPHA_FRAME(alphaDisplay); + for (int i = 0; i < locals.nSortedSegLayout; i++) + { + const core_tLCDLayout layout = locals.sortedSegLayout[i]; + const int type = MIN(layout.type & CORE_SEGALL, 9); + const int nSegs = nSegments[type]; + if (coreGlobals.nAlphaSegs) // Always return modulated value if available + { + int pos = CORE_MODOUT_SEG0 + layout.start * 16; + if (layout.type & CORE_SEGHIBIT) pos += 8; + for (int j = 0; j < nSegs; j++, pos++) // Loop over each segments of the current character (up to 16) + alphaStates[i].luminance[j] = coreGlobals.physicOutputState[pos].value; + } + else + { + UINT16 segs = coreGlobals.segments[layout.start].w; + if (layout.type & CORE_SEGHIBIT) segs >>= 8; + for (int j = 0; j < nSegs; j++, segs >>= 1) // Loop over each segments of the current character (up to 16) + alphaStates[i].luminance[j] = (segs & 1) ? 1.f : 0.f; + } + if ((type == CORE_SEG9) || (type == CORE_SEG98) || (type == CORE_SEG98F)) { + // Bottom half of vertical center is controlled by upper half + alphaStates[i].luminance[9] = alphaStates[i].luminance[8]; + } + if (type == CORE_SEG16R) { + // Reverse comma / dot + UINT8 v = alphaStates[i].luminance[15]; + alphaStates[i].luminance[15] = alphaStates[i].luminance[7]; + alphaStates[i].luminance[7] = v; + } + if ((type == CORE_SEG98F) || (type == CORE_SEG87F)) { + // Comma is lit if at least one segment is on + for (int j = 0; j < nSegs; j++) + if (j != 8 && alphaStates[i].luminance[j] > alphaStates[i].luminance[8]) + alphaStates[i].luminance[8] = alphaStates[i].luminance[j]; + } + } + } + + // Nothing to do for Video, 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 fix animation stuterring (for the time being, we update at 60Hz while the emulated DMD has a higher refresh rate and the user + // display can 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 (updateMask & PINMAME_STATE_REQMASK_DISPLAY_STATE) + { + for (unsigned int i = 0; i < locals.outputStateBlock->displayStates->nDisplays; i++) + { + struct core_dispLayout* layout = locals.displayStateBlocks[i].layout; + pinmame_tFrameState* frame = locals.displayStateBlocks[i].frame; + frame->updateTimestamp = now; + if (layout->fptr && (layout->type & CORE_SEGALL) == CORE_DMD) + { + } + else if (layout->fptr && (layout->type & CORE_SEGALL) == CORE_VIDEO) + { + } + } + }*/ + + return locals.outputStateBlock; +} + /*-------------------- / Draw a LED digit /---------------------*/ @@ -2191,6 +2759,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 +2829,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 +3967,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 +4095,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 +4106,57 @@ 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++; + switch (rotation) + { + case 0: + locals.displayStateBlocks[i].frame->width = layout->length; + locals.displayStateBlocks[i].frame->height = layout->start; + break; + case 1: + locals.displayStateBlocks[i].frame->width = layout->start; + locals.displayStateBlocks[i].frame->height = layout->length; + break; + case 3: + locals.displayStateBlocks[i].frame->width = layout->start; + locals.displayStateBlocks[i].frame->height = layout->length; + break; + default: + assert(FALSE); + break; + } + for (int y = 0; y < layout->start; y++) { + for (int x = 0; x < layout->length; x++) { + int pos; + switch (rotation) + { + case 0: pos = (x + y * layout->length) * 3; break; + case 1: pos = ((layout->start - 1 - y) + x * layout->start) * 3; break; + case 2: pos = (y + (layout->length - 1 - x) * layout->start) * 3; break; + } + palette_get_color(bitmap->read(bitmap, x, y), + &locals.displayStateBlocks[i].frame->frameData[pos ], + &locals.displayStateBlocks[i].frame->frameData[pos + 1], + &locals.displayStateBlocks[i].frame->frameData[pos + 2]); + } + } + break; + } + } + } #endif } diff --git a/src/wpc/core.h b/src/wpc/core.h index 401134264..76582f27e 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 "../libpinmame/pinmamedef.h" + /*-- some convenience macros --*/ #ifndef FALSE #define FALSE (0) @@ -172,24 +176,24 @@ / Generic Display layout data /------------------------------*/ /* The different kind of display units */ -#define CORE_SEG16 0 // 16 segments -#define CORE_SEG16R 1 // 16 segments with comma and period reversed -#define CORE_SEG10 2 // 9 segments and comma -#define CORE_SEG9 3 // 9 segments -#define CORE_SEG8 4 // 7 segments and comma -#define CORE_SEG8D 5 // 7 segments and period -#define CORE_SEG7 6 // 7 segments -#define CORE_SEG87 7 // 7 segments, comma every three -#define CORE_SEG87F 8 // 7 segments, forced comma every three -#define CORE_SEG98 9 // 9 segments, comma every three -#define CORE_SEG98F 10 // 9 segments, forced comma every three -#define CORE_SEG7S 11 // 7 segments, small -#define CORE_SEG7SC 12 // 7 segments, small, with comma +#define CORE_SEG16 0 // 14 segments with comma and period +#define CORE_SEG16R 1 // 14 segments with comma and period reversed +#define CORE_SEG10 2 // 9 segments and comma +#define CORE_SEG9 3 // 9 segments +#define CORE_SEG8 4 // 7 segments and comma +#define CORE_SEG8D 5 // 7 segments and period +#define CORE_SEG7 6 // 7 segments +#define CORE_SEG87 7 // 7 segments, comma every three +#define CORE_SEG87F 8 // 7 segments, forced comma every three +#define CORE_SEG98 9 // 9 segments, comma every three +#define CORE_SEG98F 10 // 9 segments, forced comma every three +#define CORE_SEG7S 11 // 7 segments, small +#define CORE_SEG7SC 12 // 7 segments, small, with comma #define CORE_SEG16S 13 // 16 segments with split top and bottom line #define CORE_DMD 14 // DMD Display #define CORE_VIDEO 15 // VIDEO Display -#define CORE_SEG16N 16 // 16 segments without commas -#define CORE_SEG16D 17 // 16 segments with periods only +#define CORE_SEG16N 16 // 14 segments (no dot nor comma) +#define CORE_SEG16D 17 // 14 segments with period but no comma #define CORE_SEGALL 0x1f // maximum segment definition number #define CORE_IMPORT 0x20 // Link to another display layout @@ -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/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; } diff --git a/src/wpc/wpc.c b/src/wpc/wpc.c index 8e956c849..7f13678dc 100644 --- a/src/wpc/wpc.c +++ b/src/wpc/wpc.c @@ -793,16 +793,14 @@ WRITE_HANDLER(wpc_w) { { //static double prev; printf("WPC_ALPHA2LO %8.5fms %02x %02x\n", timer_get_time() - prev, wpc_data[WPC_ALPHAPOS], data); prev = timer_get_time(); wpclocals.alphaSeg[20+wpc_data[WPC_ALPHAPOS]].b.lo |= data; - if (options.usemodsol & (CORE_MODOUT_ENABLE_PHYSOUT_ALPHASEGS | CORE_MODOUT_FORCE_ON)) - core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (20 + wpc_data[WPC_ALPHAPOS]) * 2 * 8, data); + core_write_pwm_output_8b(CORE_MODOUT_SEG0 + (20 + wpc_data[WPC_ALPHAPOS]) * 2 * 8, data); } break; case WPC_ALPHA2HI: if ((core_gameData->gen & GENWPC_HASDMD) == 0) { wpclocals.alphaSeg[20+wpc_data[WPC_ALPHAPOS]].b.hi |= data; - if (options.usemodsol & (CORE_MODOUT_ENABLE_PHYSOUT_ALPHASEGS | CORE_MODOUT_FORCE_ON)) - core_write_pwm_output_8b(CORE_MODOUT_SEG0 + ((20 + wpc_data[WPC_ALPHAPOS]) * 2 + 1) * 8, data); + core_write_pwm_output_8b(CORE_MODOUT_SEG0 + ((20 + wpc_data[WPC_ALPHAPOS]) * 2 + 1) * 8, data); } break; case WPC_LAMPROW: /* row and column can be written in any order */ @@ -869,17 +867,14 @@ WRITE_HANDLER(wpc_w) { // The delay between setting segments then blanking them is used to a rough PWM dimming // Overall timing is 1ms maximum per digit over a 16ms period //static double prev; printf("WPC_ALPHAPOS %8.5fms %02x\n", timer_get_time() - prev, data); prev = timer_get_time(); - if (options.usemodsol & (CORE_MODOUT_ENABLE_PHYSOUT_ALPHASEGS | CORE_MODOUT_FORCE_ON)) - { - int prevIndex = CORE_MODOUT_SEG0 + wpc_data[WPC_ALPHAPOS] * 2 * 8; - int newIndex = CORE_MODOUT_SEG0 + data * 2 * 8; - if (prevIndex != newIndex) - for (int i = 0; i < 4; i++) - { - int offst = i == 0 ? 0 : i == 1 ? 8 : i == 2 ? 320 : 328; - core_write_pwm_output_8b(newIndex + offst, coreGlobals.binaryOutputState[(prevIndex + offst) >> 3]); - core_write_pwm_output_8b(prevIndex + offst, 0); - } + int prevIndex = CORE_MODOUT_SEG0 + wpc_data[WPC_ALPHAPOS] * 2 * 8; + int newIndex = CORE_MODOUT_SEG0 + data * 2 * 8; + if (prevIndex != newIndex) { + for (int i = 0; i < 4; i++) { + int offst = i == 0 ? 0 : i == 1 ? 8 : i == 2 ? 320 : 328; + core_write_pwm_output_8b(newIndex + offst, coreGlobals.binaryOutputState[(prevIndex + offst) >> 3]); + core_write_pwm_output_8b(prevIndex + offst, 0); + } } } break; /* just save position */ @@ -896,8 +891,7 @@ WRITE_HANDLER(wpc_w) { else if ((core_gameData->gen & GENWPC_HASDMD) == 0) // WPC_ALPHA1LO { wpclocals.alphaSeg[wpc_data[WPC_ALPHAPOS]].b.lo |= data; - if (options.usemodsol & (CORE_MODOUT_ENABLE_PHYSOUT_ALPHASEGS | CORE_MODOUT_FORCE_ON)) - core_write_pwm_output_8b(CORE_MODOUT_SEG0 + wpc_data[WPC_ALPHAPOS] * 2 * 8, data); + core_write_pwm_output_8b(CORE_MODOUT_SEG0 + wpc_data[WPC_ALPHAPOS] * 2 * 8, data); } break; case WPC_EXTBOARD3: /* WPC_ALPHA1HI */