From 6249bac891b01c07f595f125af06f986e11d74d8 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 26 May 2026 12:21:26 -0400 Subject: [PATCH] dos: Use INT 0x21, operation 0x62 to calculate basepath. Previously this captured argv[0] and pushed it through searchpath(). It's probably better to go right to the source to find this info, though. --- src/filesystem/dos/SDL_sysfilesystem.c | 85 +++++++++++++++++++------- src/main/dos/SDL_sysmain_runapp.c | 4 -- 2 files changed, 64 insertions(+), 25 deletions(-) diff --git a/src/filesystem/dos/SDL_sysfilesystem.c b/src/filesystem/dos/SDL_sysfilesystem.c index 77a4ef4d24..fbc897dae7 100644 --- a/src/filesystem/dos/SDL_sysfilesystem.c +++ b/src/filesystem/dos/SDL_sysfilesystem.c @@ -22,9 +22,10 @@ #ifdef SDL_FILESYSTEM_DOS -#include #include +#include "../../core/dos/SDL_dos.h" + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // System dependent filesystem routines @@ -32,33 +33,75 @@ char *SDL_SYS_GetBasePath(void) { - extern const char *SDL_argv0; // from src/main/dos/SDL_sysmain_runapp.c - char *searched = searchpath(SDL_argv0); - if (!searched) { - SDL_SetError("argv[0] not found by searchpath"); - return NULL; - } + /* As of MS-DOS 3.0, you can get the full path to the EXE from the very end of the + environment table, which is discovered through the PSP: + https://en.wikipedia.org/wiki/Program_Segment_Prefix - char *fullpath = SDL_strdup(searched); - if (!fullpath) { - return NULL; - } + An EXE is launched with the PSP's segment in DS, but we'll use a DOS API to obtain it + later, in case we don't control startup. https://stanislavs.org/helppc/int_21-62.html - // I don't know if this is a good idea. Drop DOS path separators, use Unix style instead. - char *ptr; - for (ptr = fullpath; *ptr; ptr++) { - if (*ptr == '\\') { - *ptr = '/'; + The table pointed to by the word at offset 0x2C in the PSP is just a collection + of ASCIZ strings, with a blank string signifying the end. The EXE path is + after that. + + https://stackoverflow.com/questions/60928997/how-to-get-environment-variables-in-a-dos-assembly-program + */ + __dpmi_regs regs; + regs.x.ax = 0x6200; /* get PSP address */ + regs.x.bx = 0x0000; + __dpmi_int(0x21, ®s); + + const Uint32 pspseg = (Uint32) regs.x.bx; + const Uint32 envsel = (Uint32) DOS_PeekUint16((pspseg << 16) | 0x2C); + + int zero_count = 0; + int offset; + for (offset = 0; (offset < 0xFFFF) && (zero_count < 2); offset++) { + const char ch = (char) _farpeekb(envsel, offset); + if (ch == 0) { + zero_count++; + } else { + zero_count = 0; } } - // drop the .exe name. - ptr = SDL_strrchr(fullpath, '/'); - if (ptr) { - ptr[1] = '\0'; + if (zero_count != 2) { + return NULL; // uhoh + } else if (_farpeekw(envsel, offset) < 1) { // there's a Uint16 here that represents number of extension strings. In practice it's always 1 and that one string is the path we need. + return NULL; // uhoh } - return fullpath; + offset += 2; + + int slen = 0; + for (unsigned long i = (unsigned long) offset; _farpeekb(envsel, i) != 0; i++) { + slen++; + } + + slen++; /* count the null terminator. */ + + char *lastbackslash = NULL; + char *retval = (char *) SDL_malloc(slen); + if (retval) { + for (int i = 0; i < slen; i++) { + const char ch = (char) _farpeekb(envsel, offset + i); + if (ch == '\\') { + retval[i] = '/'; // I don't know if this is a good idea. Drop DOS path separators, use Unix style instead. + lastbackslash = &retval[i]; + } else { + retval[i] = ch; + } + } + } + + if (lastbackslash) { + lastbackslash[1] = '\0'; /* chop off exe name, just leave path */ + } else { // uh...should have been a full path...?! + SDL_free(retval); + retval = NULL; + } + + return retval; } char *SDL_SYS_GetPrefPath(const char *org, const char *app) diff --git a/src/main/dos/SDL_sysmain_runapp.c b/src/main/dos/SDL_sysmain_runapp.c index e41628cfce..da110b37aa 100644 --- a/src/main/dos/SDL_sysmain_runapp.c +++ b/src/main/dos/SDL_sysmain_runapp.c @@ -31,8 +31,6 @@ #include int _crt0_startup_flags = _CRT0_FLAG_LOCK_MEMORY | _CRT0_FLAG_NONMOVE_SBRK; -const char *SDL_argv0 = NULL; - int SDL_RunApp(int argc, char *argv[], SDL_main_func mainFunction, void *reserved) { (void)reserved; @@ -43,8 +41,6 @@ int SDL_RunApp(int argc, char *argv[], SDL_main_func mainFunction, void *reserve return 1; } - SDL_argv0 = argv ? argv[0] : NULL; - return SDL_CallMainFunction(argc, argv, mainFunction); }