Undocumented DOS Programming


Remember that this is undocumented DOS, not undocumented DOS under a Windows DOS box. Even though some of these work under a Windows 9x DOS box, some and most likely all will not work under a Windows NT box, including Windows 2000.

INTERRUPT 21h
Service 37h subservice 00h returns the default command line switch char. in DL. (/ or -)

Service 34h returns the address of the Critical Section Flag or InDOS flag address in ES:BX. In DOS 2.x, the Critical Error Flag is the byte after while in DOS 3.x the Critical Error Flag is before the InDOS flag byte. (In DOS 3.x, use service 5Dh subservice 06h to get the address of this Critical Error Flag).
If both of these flags are clear, it is OK to re-enter DOS. If the InDOS flag is non-zero then DOS is busy and your TSR should wait to use INT 21h.

Service 37h subservice 01h sets the default command line switch char. to the char specified in DL.

Service 52h returns the address of DOS's internal list of tables and lists, AKA the List of Lists in DOS 2.x and higher.
This List of Lists contain things like address of first MCB, number of logical disks, and other items. This list starts at 12 bytes before the actual address returned and goes up to 71 bytes after the returned address depending on the current DOS version.
If you have a copy of "UNDOCUMENTED DOS" by Andrew Schulman, you can look on page 518 for this list in detail.

Service 53h converts a BPB (BIOS-Parameter Block) to a DPB (Driver Paramter Block) (DOS 2+)
- on entry
 AH = 53h
 DS:SI <- of BPB to be converted
 ES:BP <- of new DPB
- on return
 Carry if error

Service 55h Creates a new PSP (Program Segment Prefix) (DOS 2+)
- Service 55h of Interrupt 21h (DOS 2.0+) - on entry
 AH = 55h
 DX = new segment of PSP  CX = old segment of PSP - on return
 Carry if error

Service 60h returns a 'qualified' path/filename in DI from the 'unqualified' path/filename is SI. DI is a buffer of 128 or more. This service will set all slashes to back-slashes; it will put a drive specification at from the path/filename if needed; and it will change all to upper case. It will not check for file existences though. DOS 3.x+

Service 69h gets/sets the disk serial number in DOS 4.x. You clear AL to 00h to get the disk information (see below) or set AL to 01h to set the disk information. Set BL to the drive (0=default, 1=a, 2=b, etc.), DS:DX points to the disk info buffer.
This buffer has the format of:
Offset   Size   Description
  00h    word    info level (00h)
  02h   dword    disk serial number (binary)
  06h  11 bytes  volume label or "NO NAME    " if none present
  11h   8 bytes  "FAT12   " or "FAT16   " (only if AL=00h)
See Getting the Serial Number for the documented way of getting the serial number. (This documented way doesn't allow you to set the info, only get the info.)

INTERRUPT 29h
Interrupt 29h will write the char in AL to the screen faster than service 02h or 09h of int 21h though is identical (not counting redirection) to service 02h of int 21h (but much faster).

INTERRUPT 2Eh
Interrupt 2Eh will execute a DOS command just as if you typed it in at the DOS prompt. You give the command to execute in DS:SI with the first byte specifying the length the string including a CR (enter) char at the end of the string. This interrupt destroys all registers including SS:SP. Also, make sure you have freed enough memory for the COMMAND interpreter and the command to execute. (DOS 2.0+) Do not try this in a Windows NT (2000) DOS box.

INTERRUPT 2Fh
Service 1212h returns the length of an asciiz string including the null char. (DOS 3.x)
On Entry:
  AL = 12h
  AH = 12h
  ES:DI -> asciiz string
On Return:
  CX = length including the null ending char

Service 1213h uppercase character. (DOS 3.x)
On Entry:
  AL = 12h
  AH = 13h
  last word put on stack is char to upper case
On Return:
  AL = upper case char
Note: Push a value on to the stack, call this service, then pop
from the stack, or add sp,2
Note: To uppercase a char if you know that it is already a lowercase char,
just AND the byte with 11011111b. However, I include this service because it
is undocumented. Why else?

Service 121Eh compare filenames. (DOS 3.x)
On Entry:
  AX = 121Eh
  DS:SI --> first asciiz filename
  ES:DI --> second asciiz filename
On Return:
  ZF = set if equal
Note: you don't have to use filenames. Why not use any two asciiz strings?
DOS won't know the difference!


SAMPLE CODE FOR ABOVE:
 ; assemble with NBASM

.MODEL tiny
.code
start:     .start            ; frees the desire memory we want
           mov  ax,3700h     ; returns default switch char / or -
           int  21h          ;
           mov  ah,02        ;   subservice 01h sets
           int  21h          ;         (dl=char to set)

           mov  si,offset CRLF
           call prtstring

           mov  ah,60h              ; will change an 'unqualified' filename
           mov  si,offset Unqulname ; to a 'qualified' filename.
           mov  di,offset Qulname   ; i.e.:  put a drive letter and a
           int  21h                 ; full colon in front of (if none)
           mov  si,offset Unqulname ; will also make all forward-
           call prtstring           ; slashes to back-slashs.
           mov  si,offset CRLF      ; does no check for file existence
           call prtstring           ; all upper case
           mov  si,offset Qulname   ;  DOS 3.x+
           call prtstring
           mov  si,offset CRLF           
           call prtstring                
      
           mov  al,'U'       ; FAST write char
           int  29h          ; writes a char to the screen
           mov  al,'N'       ; exactly like service 02h and 09h
           int  29h          ;  of int 21h except much faster
           mov  al,'D'       ; DOS 2.x+
           int  29h          ;
           mov  al,'o'       ;
           int  29h          ;
           mov  al,'c'       ;
           int  29h          ;
      
           mov  si,offset DirStr ; Execute a command using BASE LEVEL
           push ds           ; COMMAND Interpreter (DOS 2.0+)
           push es           ; destroys all registers
           push bp           ; including sp and ss
           cli               ; the string to execute should have
           mov  SaveSS,ss    ;   a single byte at the first with
           mov  SaveSP,sp    ;   the length of the string and the
           sti               ;   string should have a CR at the end
           int  2Eh          ;
           cli               ;  This command executes a command
           mov  ss,SaveSS    ;  just like if you typed it in at the
           mov  sp,SaveSP    ;  DOS prompt.
           sti               ;
           pop  bp           ;    Does NOT return to your program
           pop  ds           ;     in a Windows 95 DOS session.
           pop  es           ;  Make SURE you free enough memory
                             ; to run the COMMAND.COM and the command
                             ; you are going to run.

Done:      mov  ah,4Ch       ; exit to DOS
           int 21h           ; 

Prtstring  proc near uses ax
Ps1:       mov  dl,[si]      ; Get character
           inc  si           ; Point to next one
           or   dl,dl        ; End of string?
           jz   short ps2    ; Yes, so exit
           mov  ah,02h       ; Output a character
           int  21h
           jmp  short Ps1    ; Keep doing it
Ps2:       ret
Prtstring  endp

Unqulname  db  'dos/utils\edit.com',0
Qulname    dup 128,0
CRLF       db  13,10,0
BPBbuffer  dup 25,0
SaveSS     dw  00h
SaveSP     dw  00h
DirStr     db  7           ; size of command line string (not counting CR)
           db  'dir /-p'   ; command string to execute
           db  13          ; need the CR (as if we entered it on the command line)

.end  start


Also see program name for another Undocumented routine.

The BIOS Name is at F000:FF59h. It has an unknown length but is in an ASCIIZ string.

The BIOS Date is at F000:FFF5h. It is 8 bytes long and has the form of: MM/DD/YY. There is a NULL byte after it if you want to use it as an ASCIIZ string.

The BIOS Time is at F000:FFE0h. It is 8 bytes long and has the form of: HH/MM/SS. There is a NULL byte after it if you want to use it as an ASCIIZ string.

These locations are not all the same for all BIOS's.

Here is a small snippet of code to print them:
 ; assemble with NBASM

.model tiny
.code
.186

           org  100h            ; COM files start at 100h

           mov  dx,offset NNstr ; print the NameNumber message
           mov  ah,09
           int  21h

           push ds
           mov  bx,0F000h       ; segment working with
           mov  ds,bx
           mov  si,0FF59h
@@:        lodsb                ; get the byte
           or   al,al
           jz   short @f
           int  29h             ; fast print it
           jmp  short @b        ;

@@:        pop  ds
           mov  dx,offset BDate ; print the date message
           mov  ah,09
           int  21h
           push ds              ; save DS
           mov  cx,08           ; date is 8 bytes long
           mov  si,0FFF5h       ; move pointer to F000:FFF5h
           mov  ds,bx
@@:        lodsb                ; get the byte
           int  29h             ; fast print it
           loop @b              ; loop 8 times
           pop  ds

           mov  dx,offset BTime ; print the Time message
           mov  ah,09
           int  21h
           push ds
           mov  ds,bx
           mov  si,0FFE0h
           mov  cx,08
@@:        lodsb                ; get the byte
           int  29h             ; fast print it
           loop @b              ; loop 8 times
           pop  ds              ; restore DS

           mov  al,13           ; CR
           int  29h
           mov  al,10           ; LF
           int  29h
        
           int  20h             ; exit to DOS

BDate      db   13,10,'BIOS Date:  ',36
NNstr      db   13,10,'BIOS Name:  ',36
BTime      db   13,10,'BIOS Time:  ',36

.end

If you have any other questions or see a mistake that I made, e-mail me and I will do my best to help out.