• [PATCH v2] stub: ignore host exit status

    From [via djgpp@delorie.com]" @21:1/5 to J.W. Jagersma on Fri Nov 25 00:26:51 2022
    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 be
  • From [via djgpp@delorie.com]" @21:1/5 to J.W. Jagersma on Tue Jan 24 19:10:21 2023
    Would appreciate any feedback on this. I know there's a small mistake in one comment, but the code works.

    On 2022-11-25 00:26, J.W. Jagersma wrote:
    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


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)