You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
INT 21h/4401h should report invalid function for nonzero DH #266
Please confirm whether FreeDOS intentionally differs from the documented DOS contract here. If not, consider returning invalid function for this invalid AL/DH combination. The conformance fixture keeps the reference-based AX=1 expectation.
The comparison uses only the public source revision and public reference; it contains no private media or trace data.
It seems a295a75f964 set it that way, unfortunately this repo doesn't have the full history but FDPP does (dosemu2/fdpp@a295a75)
git show a295a75
commit a295a75f964db28a0e8f58e4682c6ea5b5e61d37
Author: bartoldeman <bartoldeman@6ac86273-5f31-0410-b378-82cca8765d1b>
Date: Tue Apr 13 20:37:30 2004 +0000
Correct int21/ax=4401: returned AL = old value, should set CY and DE_INVLDDATA
if DH!=0 (RBIL is a little confusing here).
git-svn-id: https://svn.code.sf.net/p/freedos/svn/kernel/trunk@878 6ac86273-5f31-0410-b378-82cca8765d1b
diff --git a/kernel/ioctl.c b/kernel/ioctl.c
index 2b314bbc..cb1334d2 100644
--- a/kernel/ioctl.c+++ b/kernel/ioctl.c@@ -106,10 +106,17 @@ COUNT DosDevIOctl(lregs * r)
/* can't set the status of a file. */
if (!(flags & SFT_FDEVICE))
return DE_INVLDFUNC;
+ /* RBIL says this is only for DOS < 6, but MSDOS 7.10 */+ /* returns this as well... and some buggy program relies*/+ /* on it :( */+ if (r->DH != 0)+ return DE_INVLDDATA;+ /* Undocumented: AL should get the old value */+ r->AL = s->sft_flags_lo;
/* Set it to what we got in the DL register from the */
/* user. */
- r->AL = s->sft_flags_lo = SFT_FDEVICE | r->DL;+ s->sft_flags_lo = SFT_FDEVICE | r->DL;
break;
case 0x02:
The document linked to earlier isn't explicit about which error code is returned on DH != 0. I suspect we need to test various DOSes to confirm.
────────────────────────────────────────────────────────────────────────────
IOCtl Data (Function 44H, Codes 0 and 1)
────────────────────────────────────────────────────────────────────────────
Call:
AH = 44H
AL
0 = Get device data
1 = Set device data
BX
Handle
DX
Device data (see text)
Return:
Carry set:
AX
1 = Invalid function
6 = Invalid handle
Carry not set:
DX
Device data
Comments:
Function 44H, Codes 0 and 1, either gets or sets the data MS-DOS uses to
control the device. AL must contain 0 to get the data or 1 to set it. BX
must contain the handle. If AL is 1, DH must contain 0.
Bitfields for device information word:
Bit(s) Description (Table 01423)
character device
14 device driver can process IOCTL requests (see AX=4402h"DOS 2+")
13 output until busy supported
11 driver supports OPEN/CLOSE calls
8 ??? (set by MS-DOS 6.2x KEYB)
7 set (indicates device)
6 EOF on input
5 raw (binary) mode
4 device is special (uses INT 29)
3 clock device
2 NUL device
1 standard output
0 standard input
disk file
15 file is remote (DOS 3.0+)
14 don't set file date/time on closing (DOS 3.0+)
11 media not removable
8 (DOS 4 only) generate INT 24 if no disk space on write or read past
end of file
7 clear (indicates file)
6 file has not been written
5-0 drive number (0 = A:)
SeeAlso: INT 29
Testing on MS-DOS v5 reveals that indeed it doesn't allow to set this bit, it checks for DH exactly equal to zero.
For the set function 4401h, the list tells us it is only valid for character devices and that "(DH must be zero for DOS version prior to 6.x)". However, this "version prior to v6" is apparently valid only for v5. v4 does allow DH = 1 and only uses DH for disk file handles, ignoring DL on those.
It only returns DH = AH = 00h, DL = AL = SFT flags for 4400h on disk file handles, DH = AH = from device attributes high byte, DL = AL = SFT flags for 4400h on character device handles.
On function 4401h, error 06h is returned if not a valid handle at all, error 0Dh if DH != 0 on input, error 01h if the handle refers to a disk file, and after that the low byte of SFT flags is set to DL | 80h (character device bit is forced set). Returned AL in case of no errors is prior SFT flags.
I wonder where the disk file bits 11, 14, 15 made it into the function 4400h description in the list. Out of MS-DOS v4.00, v4.01, v5, v7.10, all of them always return the high byte in AH and DH as all-zeroes for a disk file.
Activity
andrewbird commentedon Oct 8, 2026
It seems a295a75f964 set it that way, unfortunately this repo doesn't have the full history but FDPP does (dosemu2/fdpp@a295a75)
git show a295a75 commit a295a75f964db28a0e8f58e4682c6ea5b5e61d37 Author: bartoldeman <bartoldeman@6ac86273-5f31-0410-b378-82cca8765d1b> Date: Tue Apr 13 20:37:30 2004 +0000 Correct int21/ax=4401: returned AL = old value, should set CY and DE_INVLDDATA if DH!=0 (RBIL is a little confusing here). git-svn-id: https://svn.code.sf.net/p/freedos/svn/kernel/trunk@878 6ac86273-5f31-0410-b378-82cca8765d1b diff --git a/kernel/ioctl.c b/kernel/ioctl.c index 2b314bbc..cb1334d2 100644 --- a/kernel/ioctl.c +++ b/kernel/ioctl.c @@ -106,10 +106,17 @@ COUNT DosDevIOctl(lregs * r) /* can't set the status of a file. */ if (!(flags & SFT_FDEVICE)) return DE_INVLDFUNC; + /* RBIL says this is only for DOS < 6, but MSDOS 7.10 */ + /* returns this as well... and some buggy program relies*/ + /* on it :( */ + if (r->DH != 0) + return DE_INVLDDATA; + /* Undocumented: AL should get the old value */ + r->AL = s->sft_flags_lo; /* Set it to what we got in the DL register from the */ /* user. */ - r->AL = s->sft_flags_lo = SFT_FDEVICE | r->DL; + s->sft_flags_lo = SFT_FDEVICE | r->DL; break; case 0x02:RBIL doesn't document the error code to be used. https://fd.lod.bz/rbil/interrup/dos_kernel/214401.html#sect-2861
The document linked to earlier isn't explicit about which error code is returned on DH != 0. I suspect we need to test various DOSes to confirm.
andrewbird commentedon Oct 8, 2026
Checking the MS-DOS v4 source, it seems to be correct as is, but could do with a second opinion?
https://github.com/microsoft/MS-DOS/blob/2d04cacc5322951f187bb17e017c12920ac8ebe2/v4.0/src/DOS/IOCTL.ASM#L247-L271
However my 'DOS and BIOS functions quick reference' (QUE) says it should be AX=1, it's here https://archive.org/details/dosbiosfunctions0000unse/page/112/mode/2up
ecm-pushbx commentedon Oct 8, 2026
DH equal to 1 is explicitly commented and coded as allowed by this MS-DOS code, and is not a no-op. This is bit 8 (mask 100h) in the device info word
That bit is not defined in the list nor in the FreeDOS header:
kernel/hdr/sft.h
Lines 108 to 132 in d6791ad
Perhaps MS-DOS v5 or v6 changed this code to prohibit DH equal to 1? Should be easy enough to test.
However, it is used by MS-DOS (at least v4.00, v4.01, and the lMS-DOS kernel based on those) in exactingly one spot: https://hg.pushbx.org/ecm/tlsfiles/file/00e7de020891/msdos4/msbio.tls#l100604 (source text at https://hg.pushbx.org/ecm/msdos4/file/31d10ff75cb8/src/DOS/disk3.nas#l649 in current lDOS). And it is defined in the sf.mac file: https://hg.pushbx.org/ecm/msdos4/file/31d10ff75cb8/src/INC/sf.mac#l106
ecm-pushbx commentedon Oct 8, 2026
Regardless the exact behaviour, it warrants an update to the list. Do you want to prepare a pull request, or should I do so?
The disk3.nas code probably should be studied in more detail to figure out when it is called and what it does.
andrewbird commentedon Oct 8, 2026
I'm not good at words, so I'm sure it'll be better if you do it. Thanks.
ecm-pushbx commentedon Oct 8, 2026
Interestingly, the list does document that flag, but only for disk file handles and only in function 4400h, not in the SFT layout: https://github.com/LoopZ/TheList/blob/ff4a52aa/source/Interrupt%20List/INT%2021%20DOS%20Function%20Calls/INT%20214400%20DOS%202%20IOCTL%20GET%20DEVICE%20INFORMATION.txt
Testing on MS-DOS v5 reveals that indeed it doesn't allow to set this bit, it checks for DH exactly equal to zero.
ecm-pushbx commentedon Oct 8, 2026
For the set function 4401h, the list tells us it is only valid for character devices and that "(DH must be zero for DOS version prior to 6.x)". However, this "version prior to v6" is apparently valid only for v5. v4 does allow DH = 1 and only uses DH for disk file handles, ignoring DL on those.
Reference: https://github.com/LoopZ/TheList/blob/ff4a52aa8cefb82b02ceada68ef4d8f4647c3fb2/source/Interrupt%20List/INT%2021%20DOS%20Function%20Calls/INT%20214401%20DOS%202%20IOCTL%20SET%20DEVICE%20INFORMATION.txt
ecm-pushbx commentedon Oct 8, 2026
MS-DOS v7.10 seems to use the same code as v5.
It only returns DH = AH = 00h, DL = AL = SFT flags for 4400h on disk file handles, DH = AH = from device attributes high byte, DL = AL = SFT flags for 4400h on character device handles.
On function 4401h, error 06h is returned if not a valid handle at all, error 0Dh if DH != 0 on input, error 01h if the handle refers to a disk file, and after that the low byte of SFT flags is set to
DL | 80h(character device bit is forced set). Returned AL in case of no errors is prior SFT flags.I wonder where the disk file bits 11, 14, 15 made it into the function 4400h description in the list. Out of MS-DOS v4.00, v4.01, v5, v7.10, all of them always return the high byte in AH and DH as all-zeroes for a disk file.
ecm-pushbx commentedon Oct 8, 2026
Added an initial commit to the interrupt list in LoopZ/TheList#121
andrewbird commentedon Oct 8, 2026
And so much better than I could have written, thanks!
add and update 21.4400, 21.4401, MS-DOS v4 Handle_Fail_I24