Back to home page

DOS ain't dead

Forum index page

Log in | Register

Back to the board
Thread view  Mix view  Order
kerravon

E-mail

Ligao, Free World North,
16.10.2025, 10:29
 

not setting carry (Developers)

I believe unimplemented INT 21H functions with high AH don't set the carry flag, unlike ones with low AH. Instead, I think you are supposed to check AL? Now that we have the MSDOS 4.0 source code, do we know whether that is deliberate policy, and perhaps a reason for that policy, or whether it was an accident?

ecm

Homepage E-mail

Düsseldorf, Germany,
16.10.2025, 16:17

@ kerravon

not setting carry

> I believe unimplemented INT 21H functions with high AH don't set the carry
> flag, unlike ones with low AH. Instead, I think you are supposed to check
> AL? Now that we have the MSDOS 4.0 source code, do we know whether that is
> deliberate policy, and perhaps a reason for that policy, or whether it was
> an accident?

It is deliberate. The "high" functions use the MS-DOS v1 style error return, which consists of Carry Flag unchanged, al = 00h. This is why you can find information referencing an "error code" (in ax) of 7300h (FAT32 functions before MS-DOS v7.10), 7100h (LFN functions while MSW v4 isn't running), or even 6C00h (Extended Open/Create on MS-DOS v3.xx or DR-DOS up to v6.00).

To ensure that these odd "error codes" are detected properly, callers should ensure to stc before the int 21h call. Int 21h hooks ideally should preserve this Carry Flag status when chaining or calling their downlinks.

Unfortunately, this isn't properly documented much, including in the Interrupt List.

---
l

bretjohn

Homepage E-mail

Rio Rancho, NM,
16.10.2025, 23:59

@ ecm

not setting carry

> To ensure that these odd "error codes" are detected properly, callers
> should ensure to stc before the int 21h call. Int 21h hooks
> ideally should preserve this Carry Flag status when chaining or calling
> their downlinks.
>
> Unfortunately, this isn't properly documented much, including in the
> Interrupt List.

As you indicate, "ideally" INT 21h chains should preserve ALL flags (not just Carry) when passing though, and when using CF as a return flag should preserve ALL flags except Carry. This means NOT simply issuing a STC/CLC followed by a RETF 2 (I've seen that done) at the end of the handler but instead manipulating the the Carry bit in the Flags register (on the stack) for the return. This is important because an Interrupt call also manipulates the Interrupt flag for entry into the Interrupt handler and you definitely want to preserve the callers Interrupt flag on the return. The other really critical Flag you absolutely don't want to mess with is the Direction flag.

When issuing a call that uses CF for the return, the caller should always set CF before the call in case of incompatibility, and set the values of the return registers (if there are any) to some value that should never be returned by the handling routine.

That's all just good programming practice.

kerravon

E-mail

Ligao, Free World North,
17.10.2025, 07:58

@ ecm

not setting carry

> It is deliberate. The "high" functions use the MS-DOS v1 style error
> return, which consists of Carry Flag unchanged, al = 00h.

Thanks. And do we now have a definite definition of "high"?

ecm

Homepage E-mail

Düsseldorf, Germany,
17.10.2025, 09:30

@ bretjohn

not setting carry

> > To ensure that these odd "error codes" are detected properly, callers
> > should ensure to stc before the int 21h call. Int 21h hooks
> > ideally should preserve this Carry Flag status when chaining or calling
> > their downlinks.
> >
> > Unfortunately, this isn't properly documented much, including in the
> > Interrupt List.
>
> As you indicate, "ideally" INT 21h chains should preserve ALL flags (not
> just Carry) when passing though, and when using CF as a return flag should
> preserve ALL flags except Carry. This means NOT simply issuing a STC/CLC
> followed by a RETF 2 (I've seen that done) at the end of the handler but
> instead manipulating the the Carry bit in the Flags register (on the stack)
> for the return. This is important because an Interrupt call also
> manipulates the Interrupt flag for entry into the Interrupt handler and you
> definitely want to preserve the callers Interrupt flag on the return. The
> other really critical Flag you absolutely don't want to mess with is the
> Direction flag.

I agree. Unfortunately, ROM-BIOS routines that may modify CF often return with code like sti followed by retf 2. Another flag that should be preserved is the Trace Flag.

Here's some example code that returns all the arithmetic status flags other than the Overflow Flag, but preserves all the control flags (IF, DF, TF):

entry     Leave13         ; for msbio/ms96tpi.nas and msbio/msdisk.nas
entry   Leave2F
        push bp
        mov bp, sp
        push ax
        lahf
; bp + 0 = saved bp
; bp + 2 = ip
; bp + 4 = cs
; bp + 6 = fl
        mov byte [bp + 6], ah
        pop ax
        pop bp
        iret


And here's code that returns only the Carry Flag, preserving all others:


.iret_CF:
        push bp
        mov bp, sp
        rcr byte [bp + 6], 1    ; flip
        rol byte [bp + 6], 1    ; flop
        pop bp
        iret



> When issuing a call that uses CF for the return, the caller should always
> set CF before the call in case of incompatibility, and set the values of
> the return registers (if there are any) to some value that should never be
> returned by the handling routine.
>
> That's all just good programming practice.

I agree. As an example, when I call int 21h function 3306h (MS-DOS v5+ "get true version") I set up bx as zero before the call:


        xor bx, bx
        mov ax, 3306h   ; get real DOS version
        int 21h


In the same program I call the FreeDOS-originated extension call int 21h function 33FFh. It doesn't have a clear success indicator, so I prepare dx as zero and expect a non-zero returned segment in dx if the call is supported:


        mov ax,33ffh    ; get FreeDOS version string pointer
        xor dx, dx
        int 21h         ; returns DX AX
        test dx, dx
        jnz gotname
        mov dx, ds
        mov ax, msgnoname
gotname:


In MSDebug I also set bx to zero for the 3306h call.

---
l

ecm

Homepage E-mail

Düsseldorf, Germany,
17.10.2025, 10:29

@ kerravon

not setting carry

> > It is deliberate. The "high" functions use the MS-DOS v1 style error
> > return, which consists of Carry Flag unchanged, al = 00h.
>
> Thanks. And do we now have a definite definition of "high"?

This is the instructions in MS-DOS v4.01: https://hg.pushbx.org/ecm/msdos4/file/51ad27d225a8/src/DOS/DISP.ASM#l237

The variable is defined here: https://hg.pushbx.org/ecm/msdos4/file/51ad27d225a8/src/DOS/MS_TABLE.ASM#l234

VAL2 is calculated after the table: https://hg.pushbx.org/ecm/msdos4/file/51ad27d225a8/src/DOS/MS_TABLE.ASM#l409

That means all calls above ah=6Ch are high.

The same is true of MS-DOS v5.00 and presumably v6 as well. Here's disassembly of MS-DOS v5.00 running in dosemu2:


C:\>ldebug
-a
2BC9:0100 mov ah, 80
2BC9:0102 int 21
2BC9:0104 nop
2BC9:0105 int3
2BC9:0106 nop
2BC9:0107
-di 21
int 21 B001:008C (IISP)
 --> F000:EAA1 (nonstandard IISP)
 --> 011C:109E
-g 11C:109E
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2BC9 ES=2BC9 SS=2BC9 CS=011C IP=109E NV UP DI PL ZR NA PE NC
011C:109E 90                nop
-p
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2BC9 ES=2BC9 SS=2BC9 CS=011C IP=109F NV UP DI PL ZR NA PE NC
011C:109F 90                nop
-
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2BC9 ES=2BC9 SS=2BC9 CS=011C IP=10A0 NV UP DI PL ZR NA PE NC
011C:10A0 E8CC00            call    116F
-
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2BC9 ES=2BC9 SS=2BC9 CS=011C IP=10A3 NV UP DI PL ZR NA PE NC
011C:10A3 2EFF2E6A10        jmp     far [cs:106A]             CS:106A=FDC8:40EB
-
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2BC9 ES=2BC9 SS=2BC9 CS=FDC8 IP=40EB NV UP DI PL ZR NA PE NC
FDC8:40EB FA                cli
-
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2BC9 ES=2BC9 SS=2BC9 CS=FDC8 IP=40EC NV UP DI PL ZR NA PE NC
FDC8:40EC 80FC6C            cmp     ah, 6C
-
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2BC9 ES=2BC9 SS=2BC9 CS=FDC8 IP=40EF OV UP DI PL NZ AC PE NC
FDC8:40EF 77D2              ja      40C3                                jumping
-
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2BC9 ES=2BC9 SS=2BC9 CS=FDC8 IP=40C3 OV UP DI PL NZ AC PE NC
FDC8:40C3 32C0              xor     al, modrm al
-
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2BC9 ES=2BC9 SS=2BC9 CS=FDC8 IP=40C5 NV UP DI PL ZR NA PE NC
FDC8:40C5 CF                iret
-
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=2BC9 ES=2BC9 SS=2BC9 CS=2BC9 IP=0104 NV UP EI PL ZR NA PE NC
2BC9:0104 90                nop
-


Here's disassembly of MS-DOS v7.00, apparently it supports some(?) functions of ah=73h: (The interrupt list has some functions marked as MS-DOS 7 and others as FAT32 (corresponding to MS-DOS v7.10).)



lDebug connected to serial port. Enter KEEP to confirm.
= keep
^a
2D6A:0100 mov ah, 80
          B480
2D6A:0102 int 21
          CD21
2D6A:0104 nop
          90
2D6A:0105 int3
          CC
2D6A:0106 nop
          90
2D6A:0107
^r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=2D6A IP=0100 NV UP EI PL ZR NA PE NC
2D6A:0100 B480              mov     ah, 80
^tm 1
trace mode is 1 - interrupts are traced
^install indos
^r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=2D6A IP=0100 NV UP EI PL ZR NA PE NC
2D6A:0100 B480              mov     ah, 80
^t
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=2D6A IP=0102 NV UP EI PL ZR NA PE NC
2D6A:0102 CD21              int     21
^
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=00C9 IP=0FB2 NV UP DI PL ZR NA PE NC
00C9:0FB2 90                nop
^
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=00C9 IP=0FB3 NV UP DI PL ZR NA PE NC
00C9:0FB3 90                nop
^
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=00C9 IP=0FB4 NV UP DI PL ZR NA PE NC
00C9:0FB4 E8D100            call    1088
^p
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=00C9 IP=0FB7 NV UP DI PL ZR NA PE NC
00C9:0FB7 2EFF2E820F        jmp     far [cs:0F82]             CS:0F82=FF03:41E7
^t
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=FF03 IP=41E7 NV UP DI PL ZR NA PE NC
FF03:41E7 FA                cli
^
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=FF03 IP=41E8 NV UP DI PL ZR NA PE NC
FF03:41E8 80FC73            cmp     ah, 73
^
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=FF03 IP=41EB OV UP DI PL NZ AC PO NC
FF03:41EB 77D2              ja      41BF                                jumping
^t
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=FF03 IP=41BF OV UP DI PL NZ AC PO NC
FF03:41BF 32C0              xor     al, al
^
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=FF03 IP=41C1 NV UP DI PL ZR NA PE NC
FF03:41C1 CF                iret
^
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=2D6A IP=0104 NV UP EI PL ZR NA PE NC
2D6A:0104 90                nop
^a
2D6A:0107 mov ax, 3306
          B80633
2D6A:010A int 21
          CD21
2D6A:010C nop
          90
2D6A:010D int3
          CC
2D6A:010E
^tm 0
trace mode is 0 - interrupts are processed
^t
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=2D6A IP=0105 NV UP EI PL ZR NA PE NC
2D6A:0105 CC                int3
^
Unexpected breakpoint interrupt
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=2D6A IP=0106 NV UP EI PL ZR NA PE NC
2D6A:0106 90                nop
^
AX=8000 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=2D6A IP=0107 NV UP EI PL ZR NA PE NC
2D6A:0107 B80633            mov     ax, 3306
^
AX=3306 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=2D6A IP=010A NV UP EI PL ZR NA PE NC
2D6A:010A CD21              int     21
^
AX=3306 BX=0007 CX=0000 DX=1000 SP=FFFE BP=0000 SI=0000 DI=0000
DS=2D6A ES=2D6A SS=2D6A CS=2D6A IP=010C NV UP EI PL ZR NA PE NC
2D6A:010C 90                nop
^

---
l

kerravon

E-mail

Ligao, Free World North,
17.10.2025, 10:55

@ ecm

not setting carry

> That means all calls above ah=6Ch are high.

Thanks for all that effort to determine what was happening.

I have documented that here:

https://sourceforge.net/p/pdos/gitcode/ci/8274f934426a4cb78b86a92fc6fac30a07f58ef5/

ecm

Homepage E-mail

Düsseldorf, Germany,
17.10.2025, 11:31

@ kerravon

not setting carry

> > That means all calls above ah=6Ch are high.
>
> Thanks for all that effort to determine what was happening.
>
> I have documented that here:
>
> https://sourceforge.net/p/pdos/gitcode/ci/8274f934426a4cb78b86a92fc6fac30a07f58ef5/

Addendum: MS-DOS v7.00 sets ax = 7100h and CY (set Carry Flag) on eg function 716Ch when running without the MSW v4 part. So that may be part of the reason it isn't well documented that you should stc for older MS-DOS.

---
l

kerravon

E-mail

Ligao, Free World North,
17.10.2025, 16:51

@ ecm

not setting carry

> > > That means all calls above ah=6Ch are high.
> >
> > Thanks for all that effort to determine what was happening.
> >
> > I have documented that here:
> >
> >
> https://sourceforge.net/p/pdos/gitcode/ci/8274f934426a4cb78b86a92fc6fac30a07f58ef5/
>
> Addendum: MS-DOS v7.00 sets ax = 7100h and CY (set Carry Flag) on eg
> function 716Ch when running without the MSW v4 part. So that may be part of
> the reason it isn't well documented that you should stc for
> older MS-DOS.

I think that's covered by this:

Greater than 73H on MSDOS 7.0

It's only 74H and above that I am expecting to have the "carry issue"
on that particular version of MSDOS.

BFN. Paul.

ecm

Homepage E-mail

Düsseldorf, Germany,
17.10.2025, 18:43

@ kerravon

not setting carry

>
> I think that's covered by this:
>
> Greater than 73H on MSDOS 7.0
>
> It's only 74H and above that I am expecting to have the "carry issue"
> on that particular version of MSDOS.

Indeed the maximum command is 73h, but the "high" functions aren't the only ones that use MS-DOS v1 style return. So it's a valid question to study what exactly it does for function 71h.

---
l

kerravon

E-mail

Ligao, Free World North,
17.10.2025, 19:05

@ ecm

not setting carry

> >
> > I think that's covered by this:
> >
> > Greater than 73H on MSDOS 7.0
> >
> > It's only 74H and above that I am expecting to have the "carry issue"
> > on that particular version of MSDOS.
>
> Indeed the maximum command is 73h, but the "high" functions aren't the only
> ones that use MS-DOS v1 style return. So it's a valid question to study
> what exactly it does for function 71h.

Thanks - I added that factoid to the comments in my code too.

And this is all in preparation for supporting PM16. In order for my startup code to detect the environment and adjust appropriately, I will need some extra DOS functions.

I made a change to my dos startup code today already in preparation for that. pdld was updated to provide some OMF16 support. I was able to produce a tiny memory model program, and .com file, with it. There's still some issues though. Also, he agreed to convert type 3 32-bit relocations into type 9, so that I could get an executable and be in a position to post-process it later. So even large memory model now links.

Back to the board
Thread view  Mix view  Order
22752 Postings in 2119 Threads, 402 registered users (0 online)
DOS ain't dead | Admin contact
RSS Feed
powered by my little forum