psx-beetle: build on macOS and send large replies whole

Three portability fixes for the psx-beetle oracle target, none of which
changes behaviour on MinGW or Linux:

- beetle_cdc_decode_volume redeclared PSX_CDC at block scope inside an
  extern "C" function. GCC accepts that; clang gives the declaration C
  linkage and rejects it against psx.h's C++ declaration. psx.h already
  declares PSX_CDC, so drop the redeclaration.
- The debug server's client socket is non-blocking, and send_raw dropped
  the connection on the first EWOULDBLOCK. macOS starts a TCP send buffer
  at 128 KB, so a large reply (read_ram of the BIOS ROM or all of RAM)
  reached the client as a truncated JSON line. Wait for the socket to
  drain (select, 15 s budget, as debug_server.c's send_all_blocking does)
  and only drop the client on a real error.
- .gitignore had "beetle-psx/", which matches a directory but not the
  symlink docs recommend when the Beetle checkout lives outside the tree.
  "beetle-psx" matches both.

Also note the non-Windows make platforms in the target's comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Shokunin
2026-09-29 10:58:14 -07:00
parent 44a45d3c3d
commit 9ef516a7f1
4 changed files with 50 additions and 9 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ release-stage/
*.zip
# Vendored upstream sources (cloned locally, not checked in)
beetle-psx/
beetle-psx
# Explicit-fallback SDL2 / dep zips (large, regenerable)
sdl2-dev.zip
+5 -2
View File
@@ -868,14 +868,17 @@ if(NOT MSVC)
)
endif()
# ---- psx-beetle target (Beetle PSX libretro standalone, MinGW) ----
# ---- psx-beetle target (Beetle PSX libretro standalone; MinGW, Linux, macOS) ----
#
# Standalone binary that runs Beetle PSX (mednafen-psx libretro core) with
# a debug server exposing the SAME wire-protocol commands as psx-runtime,
# on a different port (4380). No recomp code linked.
#
# Requires beetle-psx built via:
# Requires beetle-psx with the docs/beetle_*.patch hooks, built via:
# cd beetle-psx && make platform=mingw_x86_64 STATIC_LINKING=1 HAVE_LIGHTREC=0
# (platform=unix on Linux, platform=osx on macOS; there the archive is named
# mednafen_psx_libretro.so/.dylib; copy it to libmednafen_psx.a). Recipes:
# docs/beetle-linux.md, docs/beetle-macos.md.
set(BEETLE_LIB "${CMAKE_SOURCE_DIR}/../beetle-psx/libmednafen_psx.a")
+41 -5
View File
@@ -15,6 +15,8 @@
#include <stdarg.h>
#include <stdint.h>
#include "host_time.h"
#ifndef DEFAULT_DEBUG_PORT
#error DEFAULT_DEBUG_PORT must be defined by the beetle runtime target.
#endif
@@ -28,6 +30,7 @@
# define sock_close closesocket
#else
# include <sys/socket.h>
# include <sys/select.h>
# include <netinet/in.h>
# include <unistd.h>
# include <fcntl.h>
@@ -189,17 +192,50 @@ static int s_input_override = -1;
static int s_input_frames = 0;
/* ---- Send helpers ---- */
/* The client socket is non-blocking. A reply larger than the free space in
* the kernel send buffer (a 2 MB read_ram is ~4 MB of hex; macOS starts a
* TCP send buffer at 128 KB) makes send() fail with EWOULDBLOCK
* part-way through. Wait for the socket to drain and carry on, like
* debug_server.c's send_all_blocking, instead of dropping the connection
* and leaving the client a truncated JSON line. */
#define SEND_TOTAL_BUDGET_MS 15000
static int send_would_block(void) {
#ifdef _WIN32
return WSAGetLastError() == WSAEWOULDBLOCK;
#else
return errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR;
#endif
}
/* Wait up to 100 ms for the client socket to become writable. */
static int send_wait_writable(void) {
fd_set wr;
FD_ZERO(&wr);
FD_SET(s_client, &wr);
struct timeval tv = { 0, 100 * 1000 };
return select((int)s_client + 1, NULL, &wr, NULL, &tv) >= 0;
}
static void send_raw(const char *data, int n) {
if (s_client == SOCK_INVALID) return;
int off = 0;
const uint64_t t_start = psx_host_mono_ms();
while (off < n) {
int k = send(s_client, data + off, n - off, 0);
if (k <= 0) {
sock_close(s_client);
s_client = SOCK_INVALID;
return;
if (k > 0) {
off += k;
continue;
}
off += k;
if (k < 0 && send_would_block() &&
psx_host_mono_ms() - t_start < SEND_TOTAL_BUDGET_MS &&
send_wait_writable()) {
continue;
}
sock_close(s_client);
s_client = SOCK_INVALID;
return;
}
}
+3 -1
View File
@@ -1018,7 +1018,9 @@ extern "C" uint64_t beetle_spu_event_total(void) { return s_spu_event_seq; }
* Returns 0 if the CDC isn't up. */
extern "C" int beetle_cdc_decode_volume(unsigned out[4])
{
extern PS_CDC *PSX_CDC;
/* PSX_CDC is declared by mednafen/psx/psx.h. A block-scope redeclaration
* here would get C language linkage from the enclosing extern "C"
* function, which clang rejects against psx.h's C++ declaration. */
if (!PSX_CDC) return 0;
uint8 m[2][2];
PSX_CDC->PSXRecomp_GetDecodeVolume(m);