Adding A Pause Between Items in Config.NT / Config.Sys

I wanted to debug startup of a 16-bit DOS driver on 32-bit Windows 10 with NTVDM, however attempts to attach debugger / Time Travel Debugging Trace to NTVDM startup process was triggering access violations and causing NTVDM.exe to crash. Once NTVDM had started I could attach debugger fine, but was missing the driver startup code I wanted to capture.

MS-DOS 6.00 added a feature where F8 could be pressed to run autoexec.bat/config.sys entries one line at a time, but I haven’t found an alternative that works with c:\windows\system32\config.nt in Windows.

In this case using Microsoft Macro Assmbler built this driver with the following commands:

masm wait.asm
link wait
exe2bin wait.exe wait.sys
xcopy wait.sys C:\windows\system32

The code is here, this can also be used a template for a simple MS-DOS driver.

; *******************************************************************
; * Press Any Key To Continue DRIVER                                *
; *******************************************************************

cseg        segment para    public  'code'
wait        proc    far
            assume  cs:cseg,es:cseg,ds:cseg

; *******************************************************************
; * MAIN PROCEDURE CODE                                             *
; *******************************************************************

begin:

; *******************************************************************
; * DEVICE HEADER - REQUIRED BY DOS                                 *
; *******************************************************************

next_dev    dd  -1              ; no other device drivers
attribute   dw  8000h           ; character device
strategy    dw  dev_strategy    ; address of 1st dos call
interrupt   dw  dev_interrupt   ; address of 2nd dos call
dev_name    db  'WAIT$ '      ; name of the driver

; *******************************************************************
; * WORK SPACE FOR THE DEVICE DRIVER                                *
; *******************************************************************

rh_ofs      dw  ?               ; request header offset
rh_seg      dw  ?               ; request header segment
msg1        db  'Waiting...'
            db  0dh,0ah,'$'
seconds     db 0   
counter     db 0  
crlf	    db 0dh,0ah,'$'
; *******************************************************************
; * THE STRATEGY PROCEDURE                                          *
; *******************************************************************

dev_strategy:                   ; first call from DOS
    mov     cs:rh_seg,es        ; save request header ptr segment
    mov     cs:rh_ofs,bx        ; save request header ptr offset
    ret

; *******************************************************************
; * THE INTERRUPT PROCEDURE                                         *
; *******************************************************************

dev_interrupt:                  ; second call from DOS
    cld                         ; save machine state on entry
    push    ds
    push    es
    push    ax
    push    bx
    push    cx  
    push    dx
    push    di
    push    si

; perform branch based on the command passed in the req header

    mov     al,es:[bx]+2        ; get command code
    cmp     al,0                ; check for 0
    jnz     exit3               ; no - exit go to error exit
    rol     al,1                ; get offset into table
    lea     di,cmdtab           ; get address of command table
    mov     ah,0                ; clear hi order
    add     di,ax               ; add offset
    jmp     word ptr[di]        ; jump indirect

; command table
;       the command code field of the static request
;       field contains the function to be performed

cmdtab  label   byte            ;
        dw      init            ; initialization

; *******************************************************************
; *     LOCAL PROCEDURES                                            *
; *******************************************************************

initial proc    near
    lea     dx,msg1             ; initialization
    mov     ah,9                ; message
    int     21h                 ; dos call
    mov     al,30               ; number of seconds to wait
    call    sleep
    ret                         ; return
initial endp

; *******************************************************************
; *     DOS COMMAND PROCESSING                                      *
; *******************************************************************

;command    0   initialization

init:   call    initial         ; display a message
        lea     ax,exit         ; get end address (offset)
        mov     es:[bx]+0eh,ax  ; store offset address
        push    cs              ; get end
        pop     ax              ; address (segment)
        mov     es:[bx]+10h,ax  ; store in break address
        jmp     exit2

; *******************************************************************
; *     ERROR EXIT                                                  *
; *******************************************************************

; Set the done flag, error flag, and unknown command error code

exit3:  mov     es:word ptr 3[bx],8103h
        jmp     exit1                   ; restore environment

; *******************************************************************
; *     COMMON EXIT                                                 *
; *******************************************************************

; common exits fall thru code
;   2 sets status to done and no error
;   1 restore callers es:bx
;   0 restore machine state and exit

exit2:                                  ; set done flag and no error
        mov     es:word ptr 3[bx],0100h
exit1:  mov     bx,cs:rh_ofs            ; restore req hdr to bx and es
        mov     es,cs:rh_seg            ; as saved by dev_Strategy
exit0:  pop     si                      ; restore all registers
        pop     di
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        pop     es
        pop     ds
        ret
exit:

; *******************************************************************
; *     END OF PROGRAM                                              *
; *******************************************************************


wait    endp

sleep proc    near

wait_for_al_seconds:      
wait_loop:
    push ax               ; save our counter (al)
    mov     [counter],al
loop_top:
    mov ah,2
    int 1ah             ; get time

    mov ah, [seconds]   ; retrieve last good value
    cmp ah, dh          ; is it same as last good value?
    jz  loop_top         ; yup, ignore it, loop again!

    mov [seconds], dh   ; save seconds

    ; display counter - can handle range of 0-99
    mov	al, [counter]   ; retrieve counter
    cbw                 ; set AH to 0
    mov  dl, 10
    div  dl             ; Divides AX by 10: quotient in al, remainder in ah
    add  ax, "00"
    mov  dx, ax         
    mov  ah, 02h        ; Display 1st digit of counter
    int  21h
    mov  dl, dh          
    int  21h            ; Display 2nd digit of counter

    
    lea     dx,crlf             ; display carriage return
    mov     ah,9                
    int     21h                 

    pop ax              
    dec al              ; decrease al by one (does not set flags!!)
    or al,al            ; set flags
    jnz wait_loop       ; al=0?  nope, around we go again!

    ret                 ; 
sleep   endp


cseg        ends
            end     begin

; that's all folks!


Now we can add line to C:\Windows\System32\config.nt to load our driver where we want it to pause:

DEVICE=%SystemRoot%\System32\wait.sys

To test all existing ntvdm.exe process must be terminated, as config.sys is only loaded when a new ntvdm.exe instance is created. Now when launching a 16-bit DOS application you will see a count down for 30 seconds when this line of config.nt has been hit:

About chentiangemalc

specializes in end-user computing technologies. disclaimer 1) use at your own risk. test any solution in your environment. if you do not understand the impact/consequences of what you're doing please stop, and ask advice from somebody who does. 2) views are my own at the time of posting and do not necessarily represent my current view or the view of my employer and family members/relatives. 3) over the years Microsoft/Citrix/VMWare have given me a few free shirts, pens, paper notebooks/etc. despite these gifts i will try to remain unbiased.
This entry was posted in Uncategorized. Bookmark the permalink.

1 Response to Adding A Pause Between Items in Config.NT / Config.Sys

  1. rich masi says:

    I need your help. an executable from win7 wont run on win10 on an industrial PC from siemens but WILL run on my laptop. event viewer etc. not showing any glaring errors. my customer is paying me, ill pay you.

Leave a comment