From: dmurdoch@mast.queensu.ca (Duncan Murdoch)
Subject: Re: Causing the compter to stop from the config.sys file
Organization: Queen's University
Date: Sat, 23 Jan 1993 23:48:13 GMT

In article <1993Jan23.173306.1751@borland.com> dave@borland.com (Dave) writes:
>In article <1993Jan21.231925.46627@kuhub.cc.ukans.edu>
>christos@kuhub.cc.ukans.edu writes:

>>        I was wondering if it is possible to cause a pause inside the 
>>config.sys file in a similar way we can do this in the autoexec.bat file.
>>I would appreciate any relevant resposne....

>Yes. Get DR DOS and you will be all set. The actual line you
>would add could be as follows:

>?REM

It hardly seems worth getting DR DOS for such a tiny little utility.  It 
took about an hour to write one in Turbo Pascal; the source code is below.  

I'll be uploading this to garbo.uwasa.fi; look for an announcement on comp.
archives.msdos.announce (I expect it'll be called pausedev.zip, and should 
end up in /pc/turbopas, since it's more of a demonstration of how to write 
a device driver than a useful utility.)

Compile it to PAUSEDEV.EXE, then put

  DEVICE=d:\path\PAUSEDEV.EXE

into your CONFIG.SYS wherever you want a pause.

Duncan Murdoch
dmurdoch@mast.queensu.ca

--------- PAUSEDEV.PAS -----------
  {$S-,F-}         { Stack checking wouldn't work here, and we assume near calls
}
  {$X+}            { Extended syntax }
  {$M $1000,0,0}   { We won't use the heap or stack. }

  program pausedev;
  { This program implements a PAUSE command either as DEVICE=PAUSEDEV.EXE
    or executed from the command line. }

  uses opint,  { OPro interrupt services, needed for stack switching }
       crt;    { Standard I/O unit that doesn't rely on DOS }

  procedure strategy_routine(bp:word); interrupt; forward;
  procedure interrupt_routine(bp:word); interrupt; forward;
  procedure call_Main_Block; forward;

  { This procedure must come first!!! }

  procedure header;
  assembler;
  asm
    dd $FFFFFFFF    { next driver }
    dw $8000        { attributes of simple character device }
    dw offset strategy_routine
    dw offset interrupt_routine
    db 'PAUSE   '
  end;

  const
    stDone = $100;
    stBusy = $200;

    cmInit = 0;

    device_driver : boolean = false;

  type
    request_header = record
      request_length : byte;
      subunit: byte;
      command_code : byte;
      status : word;
      reserved: array[1..8] of byte;
      num_units : byte;
      first_free : pointer;
      args : ^char;
      drive_num : byte;
    end;

  var
    local_stack : array[1..4000] of byte;
    end_of_stack : byte;
    request : ^request_header;

  procedure handler(var regs : intregisters);
  { This routine is called by the strategy routine, and handles all requests.
    The data segment is okay, and we're running on the local_stack so we've got
    plenty of space.  Remember:  the init sections haven't been called yet!
  }
  begin
    with request^ do
    begin
      case command_code of
      cmInit: begin
                device_driver := true;
                Call_Main_Block;
                first_free := ptr(cseg,0); { Release everything!! }
                status := stDone;
              end;
      else
        status := stBusy;
      end;
    end;
  end;

  procedure RetFar; assembler;
  { Replacement for the IRET code that ends the interrupt routines below }
  asm
    mov sp,bp
    pop bp
    pop es
    pop ds
    pop di
    pop si
    pop dx
    pop cx
    pop bx
    pop ax
    retf
  end;

  procedure strategy_routine(bp:word);
  var
    regs : intregisters absolute bp;
  begin
    with regs do
      request := ptr(es,bx);
    RetFar;
  end;

  procedure interrupt_routine(bp:word);
  var
    regs : intregisters absolute bp;
  begin
    SwapStackandCallNear(Ofs(handler),@end_of_stack,regs);
    RetFar;
  end;

  procedure do_pause;
  begin
    while keypressed do readkey;
    writeln('Press any key to continue....');
    repeat until keypressed;
  end;

  procedure Ret_From_Main_Block; assembler;  { Must have declaration just
                                                like Call_Main_Block }
  asm
  end;

  procedure Call_Main_Block; assembler;      { Must come just before main
                                                  entry point }
  asm
    db $eb, 6                                { Jump over the Ret instruction
                                               and System init }
  end;

  begin
    do_pause;

    if Device_Driver then
      asm
        pop bp
        jmp Ret_From_Main_Block;
      end
    else
      halt;

    header;    { This line is never reached, but it fools the linker
                 so that everything is loaded properly. }
  end.
Newsgroups: comp.os.msdos.programmer