These two services are how a program reads a directory: DIR uses them, file managers use them, and so does every program that takes *.TXT on its command line, since DOS does not expand wildcards; the program receives "*.TXT" as typed. 4Eh starts a search and returns the first match, 4Fh returns the next one each time it is called, and CF = 1 ends the list.
What the DTA holds
Each match is written to the disk transfer area (see INT 21h AH=1Ah), 43 bytes:
| Offset | Size | Contents |
|---|---|---|
| 00h | 21 bytes | DOS's own: the pattern, the drive, the position in the directory. 4Fh continues from here |
| 15h | byte | the attribute of the entry found |
| 16h | word | the time: hours × 2048 + minutes × 32 + seconds / 2 |
| 18h | word | the date: (year − 1980) × 512 + month × 32 + day |
| 1Ah | dword | the size in bytes, low word first |
| 1Eh | 13 bytes | the name, "NAME.EXT" and a 0 byte, without padding |
The first 21 bytes are the search's state, so 4Fh works only with the same DTA, untouched since the 4Eh. Two searches at once, such as walking a directory tree, need a DTA each, switched with 1Ah before every 4Fh.
Attributes
| Bit | Value | The entry is | In CX |
|---|---|---|---|
| 0 | 01h | read-only | ignored: such files are always found |
| 1 | 02h | hidden | include hidden entries |
| 2 | 04h | system | include system entries |
| 3 | 08h | the volume label | alone: find only the label |
| 4 | 10h | a directory | include subdirectories |
| 5 | 20h | changed since the last backup (archive) | ignored |
CX widens the search rather than filtering it: normal files are always found, and the hidden, system and directory bits add those entries to them. With CX = 10h a search returns files and directories alike, so test bit 4 of the byte at 15h to tell them apart. In a subdirectory the entries "." and ".." come first.
In the pattern, ? matches any one character and * the rest of the name or of the extension. The name and the extension are matched separately, so "*.*" finds everything, names without an extension included, and in MS-DOS "A*B.TXT" is the same as "A*.TXT": whatever follows a * in its part is ignored.
The example lists the current directory with sizes, marking subdirectories. The loop ends when 4Eh or 4Fh return CF = 1:
CLDMOV AH, 1Ah ; DOS: our own DTA, not the command tailMOV DX, OFFSET DTA1INT 21hMOV AH, 4Eh ; DOS: find the first matchMOV DX, OFFSET PAT1 ; "*.*"MOV CX, 10h ; files and directoriesINT 21hNEXT1:JC DONE1 ; CF = 1: no (more) entriesMOV SI, OFFSET DTA1+1Eh ; the name, ending with 0MOV BX, 13 ; the column is 13 wideNAME1:LODSBOR AL, ALJZ PAD1MOV DL, ALMOV AH, 02hINT 21hDEC BXJMP NAME1PAD1:MOV DL, " " ; spaces to the next columnMOV AH, 02hINT 21hDEC BXJNZ PAD1TEST BYTE PTR DTA1+15h, 10h ; the attribute: a directory?JZ SIZE1MOV DX, OFFSET DIRM1MOV AH, 09hINT 21hJMP EOL1SIZE1:MOV AX, WORD PTR DTA1+1Ah ; the size, low wordMOV DX, WORD PTR DTA1+1Ch ; and high wordCALL PDEC32EOL1:MOV DX, OFFSET CRLFMOV AH, 09hINT 21hMOV AH, 4Fh ; DOS: the next match, same DTAINT 21hJMP NEXT1DONE1:PAT1 DB "*.*", 0DIRM1 DB "<DIR>$"DTA1 DB 43 DUP(0)CRLF DB 13, 10, "$"PDEC32 PROC NEAR ; print DX:AX in decimal (changes AX BX CX DX DI)MOV BX, 10XOR CX, CXPDEC32A:PUSH AX ; DX:AX / 10 in two steps:MOV AX, DXXOR DX, DXDIV BX ; the high word firstMOV DI, AX ; the high word of the quotientPOP AXDIV BX ; then remainder:low wordPUSH DX ; a digitINC CXMOV DX, DI ; DX:AX = the quotientOR DI, AXJNZ PDEC32APDEC32B:POP DXADD DL, "0"MOV AH, 02hINT 21hLOOP PDEC32BRETPDEC32 ENDP
Errors
From 4Eh, CF = 1 with AX = 3 means the path does not exist, and AX = 2 or 18 (12h) that nothing matches: which of the two depends on the DOS version. From 4Fh, AX = 18: no more files. Both are a normal end, not a failure.
Long names in DOSBox-X
The DTA has room for 8.3 names only. A file on a host folder with a longer name shows up under a generated short name such as LONGFI~1.TXT, the same name DIR prints, and that name works with every file service. With long file names enabled, functions 714Eh and 714Fh of INT 21h return long names.