Close

DOOM on the server, and a compiler that broke it

A project log for The Arcade Cabinet That Runs Nothing

An arcade cabinet built around an M5Stack AtomS3R that plays 27 DOS games and DOOM, and runs a demo reel of 41 titles including full-motion

juha-liljaJuha Lilja 2 hours ago0 Comments

DOOM itself is doomgeneric with a small platform layer I wrote, which pushes each frame over loopback TCP to the Python service with a 16-byte header. It also sends the game state (menu, in level, dead), so the one button can mean ENTER in menus, USE when you're dead and FIRE in play.

memcpy(hdr, "DGF1", 4);
hdr[4] = (unsigned char)(DOOMGENERIC_RESX & 0xFF);
hdr[5] = (unsigned char)(DOOMGENERIC_RESX >> 8);
hdr[6] = (unsigned char)(DOOMGENERIC_RESY & 0xFF);
hdr[7] = (unsigned char)(DOOMGENERIC_RESY >> 8);
hdr[8] = 4;                       /* bytes per pixel, 0x00RRGGBB */
hdr[9] = md_game_state();

Building it with zig cc found two things. zig turns UBSan traps on by default, and DOOM does SHORT(patch->leftoffset)<<FRACBITS on a negative number during sprite init, which is undefined behaviour and has worked fine for 30 years. Trapped, it dies before the first frame. Then at -O1 and above clang miscompiles something and the game hangs in texture init with a corrupt hash chain. I never found which pass. gcc is fine, so the build script pins it:

case "$CC" in  *zig*) OPT="${MD_OPT:--O0}" ;;  *)     OPT="${MD_OPT:--O2}" ;;
esac
CFLAGS="$CFLAGS -fno-strict-aliasing"
case "$CC" in  *zig*|*clang*) CFLAGS="$CFLAGS -fno-sanitize=undefined" ;;
esac

At -O0 DOOM still renders 320x200 faster than the 35 fps it asks for. Windows also gave me a boolean typedef clash between DOOM and windows.h, and winsock defines s_host as a macro.

Discussions