Re: [PATCH] stub: ignore host exit status

9 views
Skip to first unread message

J.W. Jagersma (jwjagersma@gmail.com) [via djgpp@delorie.com]

unread,
Nov 24, 2022, 11:32:05 PM11/24/22
to dj...@delorie.com
On 2022-11-23 20:47, J.W. Jagersma wrote:
> [patch omitted]

Noticed a small bug here, sorry. Will send a v2 soon.

J.W. Jagersma (jwjagersma@gmail.com) [via djgpp@delorie.com]

unread,
Nov 24, 2022, 11:32:06 PM11/24/22
to dj...@delorie.com
On 2022-11-23 20:47, J.W. Jagersma wrote:
> Hi all,
>
> If you've ever tried using hdpmi32 with a djgpp program, you may have
> noticed some strange behavior. The program first fails to run with
> "Load error: no DPMI", but on the second try, it suddenly works. Then
> a third invocation fails again, the fourth runs fine, etc.
>
> When you rename 'hdpmi32.exe' to 'cwsdpmi.exe', your program seems to
> run consistently. But this is only because there is a second (real)
> cwsdpmi in your PATH somewhere. What really happens, is that your
> program alternates between running under hdpmi and cwsdpmi.
>
> I finally figured out why this happens, and it's very simple: hdpmi uses
> the exit status to indicate if it was loaded via xms, vcpi, or int15.
> And the djgpp stub considers any non-zero exit status to be a failure,
> so it keeps searching PATH until cwsdpmi is found, which always returns
> zero.
>
> For a possible solution, I would suggest to ignore the return status
> from the host, and just check int 2f/1687 after every exec. As an added
> bonus, this saves 13 bytes of stub space.

Amended patch follows - it no longer skips the last iteration (exec
with no path).

It's a bit larger, but still saves 6 bytes in total.

--- a/src/stub/stub.asm
+++ b/src/stub/stub.asm
@@ -199,38 +199,49 @@ not_path:
;-----------------------------------------------------------------------------
; Get DPMI information before doing anything 386-specific

- push es
push di
- xor cx, cx ; flag for load attempt set cx = 0
- jz @f2 ; We always jump, shorter than jmp
+ ; Set up loadname in case of no DPMI.
+ ; First try current dir.
+ xor ah, ah ; Copy until this character (=0)
+ call store_env_string ; copy stub image to "loadname"
+ mov si, bx ; remove name so we can add DPMI name
+ mov di, [path_off] ; Pointer to path contents (next try)
@b1:
+ xor cx, cx ; flag to signal end of loop
+@b2:
+ call check_dpmi ; int 2f, ax=1687
+ jz @f3
+ call do_exec ; copy our name to string and try load
+ jcxz @f1 ; cx=0 while we have more paths to try
+ call check_dpmi ; last chance...
+ jz @f3
mov al, 110
mov dx, msg_no_dpmi
jmpl error
-@b2:
- or cx, cx
- jnz @b1 ; we already tried load once before
- inc cx
- call load_dpmi
- jc @b1
+@f1:
+ ; Set up loadname for next attempt.
+ mov ah, ';' ; Copy until this character
+ call store_env_string ; to "loadname"
+ or al, al ; Check terminating character
+ jne @f2 ; If ';', continue
+ dec di ; else point at null for next pass.
@f2:
- mov ax, 0x1687 ; get DPMI entry point
- int 0x2f
- or ax, ax
- jnz @b2 ; if 0 then it's there
- and bl, 1 ; 32 bit capable?
- jz @b2
+ inc cx
+ cmp si, loadname ; anything there?
+ je @b2 ; final try (no path)
+ mov al, [si-1]
+ call test_delim ; is final character a path delimiter
+ je @b1
+ movb [si], '\\' ; no, add separator between path & name
+ inc si
+ jmp @b1
@f3:
- mov [modesw], di ; store the DPMI entry point
- mov [modesw+2], es
- mov [modesw_mem], si
pop di
- pop es

;-----------------------------------------------------------------------------
; Now, find the name of the program file we are supposed to load.

-; xor ah, ah ; termination character (set above!)
+; xor ah, ah ; termination char (set by check_dpmi)
call store_env_string ; copy it to loadname, set bx

mov [stubinfo_env_size], di
@@ -700,36 +711,29 @@ pm_dos:
ret

;-----------------------------------------------------------------------------
-; load DPMI server if not present
-; First check directory from which stub is loaded, then path, then default
-; On entry di points to image name
+; Check for presence of a DPMI host, and save the mode switch address.
+; Zero flag is clear on success. Clobbers ax/bx/dx.

-path_off:
- .dw 0 ; If stays zero, no path
-
-load_dpmi:
- xor ah, ah ; Copy until this character (=0)
- call store_env_string ; copy stub image to "loadname"
- mov si, bx ; remove name so we can add DPMI name
- mov di, [path_off] ; Pointer to path contents (next try)
- jmp @f2
-loadloop:
- mov ah, ';' ; Copy until this character
- call store_env_string ; to "loadname"
- or al,al ; Check terminating character
- jne @f1 ; If ';', continue
- dec di ; else point at null for next pass.
-@f1:
- cmp si, loadname ; anything there?
- je do_exec ; final try (no path) let it return
- mov al, [si-1]
- call test_delim ; is final character a path delimiter
- je @f2
- movb [si], '\\' ; no, add separator between path & name
- inc si
-@f2:
- call do_exec ; copy our name to string and try load
- jc loadloop
+check_dpmi:
+ push es
+ push cx
+ push di
+ push si
+ mov ax, 0x1687 ; get DPMI entry point
+ int 0x2f
+ mov dx, bx
+ or dl, 1 ; bx bit 0: 32-bit capable
+ xor bx, dx
+ or ax, bx ; DPMI present if ax = 0
+ jnz @f1
+ mov [modesw], di ; store the DPMI entry point
+ mov [modesw+2], es
+ mov [modesw_mem], si
+@f1:
+ pop si
+ pop di
+ pop cx
+ pop es
ret

;-----------------------------------------------------------------------------
@@ -766,13 +770,6 @@ do_exec:
int 0x21
pop di
pop es
- jc @f1 ;carry set if exec failed
-
- mov ah, 0x4d ;get return code
- int 0x21
- sub ax, 0x300 ;ah=3 TSR, al=code (success)
- neg ax ;CY, if not originally 0x300
-@f1:
jmp restore_umb ;called func. return for us.

;-----------------------------------------------------------------------------
@@ -796,10 +793,9 @@ include_umb:
@f1:
ret

-; Restore upper memory status. All registers and flags preserved.
+; Restore upper memory status. All registers preserved.

restore_umb:
- pushf
cmpb [dos_major], 5 ; Won't work before dos 5
jb @f1
push ax
@@ -815,7 +811,6 @@ restore_umb:
pop bx
pop ax
@f1:
- popf
ret

;-----------------------------------------------------------------------------
@@ -840,6 +835,8 @@ msg_no_selectors:
.db "no DPMI selectors$"
msg_no_dpmi_memory:
.db "no DPMI memory$"
+path_off:
+ .dw 0 ; Points to PATH, stays zero if none.

;-----------------------------------------------------------------------------
; Unstored Data, available during and after mode switch

J.W. Jagersma (jwjagersma@gmail.com) [via djgpp@delorie.com]

unread,
Nov 24, 2022, 11:32:08 PM11/24/22
to dj...@delorie.com
Hi all,

If you've ever tried using hdpmi32 with a djgpp program, you may have
noticed some strange behavior. The program first fails to run with
"Load error: no DPMI", but on the second try, it suddenly works. Then
a third invocation fails again, the fourth runs fine, etc.

When you rename 'hdpmi32.exe' to 'cwsdpmi.exe', your program seems to
run consistently. But this is only because there is a second (real)
cwsdpmi in your PATH somewhere. What really happens, is that your
program alternates between running under hdpmi and cwsdpmi.

I finally figured out why this happens, and it's very simple: hdpmi uses
the exit status to indicate if it was loaded via xms, vcpi, or int15.
And the djgpp stub considers any non-zero exit status to be a failure,
so it keeps searching PATH until cwsdpmi is found, which always returns
zero.

For a possible solution, I would suggest to ignore the return status
from the host, and just check int 2f/1687 after every exec. As an added
bonus, this saves 13 bytes of stub space.

--- a/src/stub/stub.asm
+++ b/src/stub/stub.asm
@@ -199,33 +199,61 @@ not_path:
;-----------------------------------------------------------------------------
; Get DPMI information before doing anything 386-specific

+ push di
+ xor cx, cx
+ ; Set up loadname in case of no DPMI.
+ ; First try current dir.
+ xor ah, ah ; Copy until this character (=0)
+ call store_env_string ; copy stub image to "loadname"
+ mov si, bx ; remove name so we can add DPMI name
+ mov di, [path_off] ; Pointer to path contents (next try)
+check_dpmi:
push es
push di
- xor cx, cx ; flag for load attempt set cx = 0
- jz @f2 ; We always jump, shorter than jmp
-@b1:
- mov al, 110
- mov dx, msg_no_dpmi
- jmpl error
-@b2:
- or cx, cx
- jnz @b1 ; we already tried load once before
- inc cx
- call load_dpmi
- jc @b1
-@f2:
+ push si
+ push cx
mov ax, 0x1687 ; get DPMI entry point
int 0x2f
+ pop cx
or ax, ax
- jnz @b2 ; if 0 then it's there
+ jnz @f1 ; if 0 then it's there
and bl, 1 ; 32 bit capable?
- jz @b2
+ jnz have_dpmi
+@f1:
+ jcxz @f2 ; cx=0 while we have more paths to try
+ mov al, 110
+ mov dx, msg_no_dpmi
+ jmpl error
+@f2:
+ pop si
+ pop di
+ pop es
+ call do_exec ; copy our name to string and try load
+ ; Set up loadname for next attempt.
+ mov ah, ';' ; Copy until this character
+ call store_env_string ; to "loadname"
+ or al,al ; Check terminating character
+ jne @f3 ; If ';', continue
+ dec di ; else point at null for next pass.
@f3:
+ inc cx
+ cmp si, loadname ; anything there?
+ je check_dpmi ; final try (no path)
+ dec cx
+ mov al, [si-1]
+ call test_delim ; is final character a path delimiter
+ je check_dpmi
+ movb [si], '\\' ; no, add separator between path & name
+ inc si
+ jmp check_dpmi
+have_dpmi:
mov [modesw], di ; store the DPMI entry point
mov [modesw+2], es
mov [modesw_mem], si
pop di
+ pop di
pop es
+ pop di

;-----------------------------------------------------------------------------
; Now, find the name of the program file we are supposed to load.
@@ -699,39 +727,6 @@ pm_dos:
int 0x31
ret

-;-----------------------------------------------------------------------------
-; load DPMI server if not present
-; First check directory from which stub is loaded, then path, then default
-; On entry di points to image name
-
- ret
-
;-----------------------------------------------------------------------------
; add the string CWSDPMI to path ending

@@ -766,13 +761,6 @@ do_exec:
int 0x21
pop di
pop es
- jc @f1 ;carry set if exec failed
-
- mov ah, 0x4d ;get return code
- int 0x21
- sub ax, 0x300 ;ah=3 TSR, al=code (success)
- neg ax ;CY, if not originally 0x300
-@f1:
jmp restore_umb ;called func. return for us.

;-----------------------------------------------------------------------------
@@ -840,6 +828,8 @@ msg_no_selectors:

J.W. Jagersma (jwjagersma@gmail.com) [via djgpp@delorie.com]

unread,
Jan 24, 2023, 4:55:00 PM (12 days ago) Jan 24
to dj...@delorie.com
Would appreciate any feedback on this. I know there's a small mistake in one
comment, but the code works.

Frank H. Sapone (emoaddict15@gmail.com) [via djgpp@delorie.com]

unread,
Jan 24, 2023, 6:40:10 PM (12 days ago) Jan 24
to dj...@delorie.com
I know you're looking for feedback but I dont use hdpmi32 and don't know of too many others that do except to run some windows console apps under dos.  Cwsdpmi works just fine for djgpp.

In any case, someone who knows what they're looking at can hopefully chime in.  Just wanted to let you know we're receiving your messages. :)

J.W. Jagersma (jwjagersma@gmail.com) [via djgpp@delorie.com]

unread,
Jan 25, 2023, 4:13:54 PM (11 days ago) Jan 25
to dj...@delorie.com
On 2023-01-25 00:36, Frank H. Sapone (emoad...@gmail.com) [via dj...@delorie.com] wrote:
> I know you're looking for feedback but I dont use hdpmi32 and don't know of too many others that do except to run some windows console apps under dos.  Cwsdpmi works just fine for djgpp.
>
> In any case, someone who knows what they're looking at can hopefully chime in.  Just wanted to let you know we're receiving your messages. :)

Thanks for the message! I think this is an important patch, hopefully someone
can review it at some point. Although I suppose this should be fixed on the
hdpmi32 side too, so newer hdpmi versions are compatible with old djgpp
programs, and new djgpp programs can work with older hdpmi versions.

As for why someone might want to use hdpmi instead of cwsdpmi, the simple
answer is speed - on the few functions I benchmarked, it's over 20 times
faster. That can add up on things like allocating memory or PM-RM switches.
Plus it implements more (almost all) DPMI 1.0 functions.

Reply all
Reply to author
Forward
0 new messages