kerravon

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

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

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

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

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

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

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

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

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

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

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. |