diff --git a/src/mame/miltonbradley/vectrex_v.cpp b/src/mame/miltonbradley/vectrex_v.cpp index 4e84ca7c..1d936d24 100644 --- a/src/mame/miltonbradley/vectrex_v.cpp +++ b/src/mame/miltonbradley/vectrex_v.cpp @@ -7,6 +7,10 @@ #include "cpu/m6809/m6809.h" +#ifdef __EMSCRIPTEN__ +#include +#endif + #define ANALOG_DELAY 8500 #define INT_PER_CLOCK 550 @@ -34,6 +38,49 @@ enum { A_Y }; +/********************************************************************* + + Web player + + The web player draws the beam itself in WebGL, and MAME renders no + picture. MAME's own list holds one T2 period of points, which is + only part of a frame when a game overruns T2 or uses T2 as a clock. + The player needs every point, once, in order, with the time in + microseconds the beam took to reach it: light depends on beam + speed. A point with z < 0 + marks a frame boundary: the program reached the address given to + vectrex_web_frame_pc. Games with no recalibrate in their frame loop + use it. The player finds other games' frames from the beam. + +*********************************************************************/ + +#ifdef __EMSCRIPTEN__ +namespace { + constexpr uint32_t WEB_RING_SIZE = 1 << 17; + constexpr double WEB_MIN_FRAME = 0.005; // seconds + struct web_point { float x, y, z, us; }; + web_point s_web_ring[WEB_RING_SIZE]; + uint32_t s_web_head = 0; + double s_web_last_mark = 0.0; + uint32_t s_web_frame_pc = 0xffffffff; + memory_passthrough_handler s_web_tap; + + inline void web_push(float x, float y, float z, float us) + { + s_web_ring[s_web_head] = web_point{ x, y, z, us }; + s_web_head = (s_web_head + 1) & (WEB_RING_SIZE - 1); + } +} + +extern "C" { + EMSCRIPTEN_KEEPALIVE void *vectrex_web_ring() { return s_web_ring; } + EMSCRIPTEN_KEEPALIVE uint32_t vectrex_web_head() { return s_web_head; } + EMSCRIPTEN_KEEPALIVE uint32_t vectrex_web_size() { return WEB_RING_SIZE; } + EMSCRIPTEN_KEEPALIVE void vectrex_web_frame_pc(uint32_t pc) { s_web_frame_pc = pc; } +} +#endif + + /********************************************************************* Lightpen @@ -123,6 +170,10 @@ uint32_t vectrex_base_state::screen_update(screen_device &screen, bitmap_rgb32 & { screen_configuration(); +#ifdef __EMSCRIPTEN__ + return 0; +#endif + /* start black */ m_vector->add_point(m_points[m_display_start].x, m_points[m_display_start].y, @@ -160,6 +211,11 @@ void vectrex_base_state::add_point(int x, int y, rgb_t color, int intensity) newpoint->y = y; newpoint->col = color; newpoint->intensity = intensity; + +#ifdef __EMSCRIPTEN__ + web_push(x / 65536.0f, y / 65536.0f, float(intensity), + float((machine().time() - m_vector_start_time).as_double() * 1e6)); +#endif } @@ -244,6 +300,20 @@ void vectrex_base_state::video_start() std::fill(std::begin(m_analog), std::end(m_analog), 0); m_point_index = 0; m_lightpen_down = 0; + +#ifdef __EMSCRIPTEN__ + if (s_web_frame_pc != 0xffffffff) + s_web_tap = m_maincpu->space(AS_PROGRAM).install_read_tap(s_web_frame_pc, s_web_frame_pc, "web_frame", + [this] (offs_t, u8 &, u8) + { + const double now = machine().time().as_double(); + if (now - s_web_last_mark >= WEB_MIN_FRAME) + { + s_web_last_mark = now; + web_push(0.0f, 0.0f, -1.0f, 0.0f); + } + }); +#endif } void vectrex_state::video_start()