From: 9125113g@lux.latrobe.edu.au (Mitch Davis)
Newsgroups: comp.lang.pascal
Subject: Re: how to write drivers?
Date: 27 Jul 92 15:28:42 GMT
Organization: La Trobe University

In article <dedmunds.711612389@sfu.ca> dedmunds@selkirk.sfu.ca (Darran Edward Edmundson) writes:
>Can someone explain in some detail how drivers are written and used
>within a program?

I'm having to assume that you mean device drivers.  Many moons ago, in
the Fidonet International Pascal Conference, someone stated that "C was
better than Pascal because you could write device drivers in C".

Well, much to our amazement, Duncan Murdoch managed to produce such a
beast, and here it is:

On: Tue 28 May 91 11:18p
By: Dj Murdoch
To: All
Re: **** Device driver in TP *****
-----------------------------------------------------------------------------
They said it couldn't be done!  (In fact, I think I was one of the 
naysayers...)
 
"TP can't create the special format of file that's needed for a device
driver."
 
Wrong!!
 
I've written a simple device driver in TP, and it works.  From some
things I've heard, it won't work in all versions of DOS (it's an .EXE 
format device driver, not a .BIN format one).  There are tons of 
restrictions on what you can do in it - DOS isn't reentrant, and the 
TP system library isn't designed to do things while DOS is active, so 
I don't even let it get initialized, etc., etc.
 
It's still a bit of a mess, but here it is, for your enjoyment and
edification:

 a character device driver that keeps a buffer of 255 characters, called
TPDEVICE.
 
To try it out, compile it (you'll need OPro or TPro; sorry, but stack
swapping is essential, and I wouldn't want to try to write code to do 
it myself), put it into your CONFIG.SYS (on a floppy disk, please!) as
 
  device=tpdev.exe
 
and then reboot.   Hopefully you won't crash, but if you do, you'll have
to reboot from a different disk and remove it from CONFIG.SYS.
 
Then you can try
 
  COPY TPDEVICE CON
 
to see the initialization message, and
 
  ECHO This is a line for the buffer >TPDEVICE
 
to replace it with a new one.
 
Enjoy!!
 
I'll be uploading TPDEV.ZIP to 1:221/177 (519-578-9314), in case the
file gets too mangled in transmission to be recompiled, or you want to 
try it out without having TPRO or OPRO.  First call downloads accepted.
 
----------------------------
  { DOS character device driver written entirely in TP 6 }
  { Written by D.J. Murdoch for the public domain, May 1991 }
  {$S-,F-}{ Stack checking wouldn't work here, and we assume near calls }
  {$M $1000,0,0}   { We can't use the heap and don't use the stack.
                     This setting doesn't really matter though, since 
                     you normally won't run TPDEV }
 
  program tpdev;
 
  uses opint;  { OPro interrupt services, needed for stack switching }
 
  procedure strategy_routine(bp:word); interrupt; forward;
  procedure interrupt_routine(bp:word); interrupt; forward;
 
  procedure header;  { Here's the trick:  an assembler routine in the
                       main program, guaranteed to be linked first in
                       the .EXE file!! }
  assembler;
  asm
    dd $FFFFFFFF    { next driver }
    dw $8000        { attributes of simple character device }
    dw offset strategy_routine
    dw offset interrupt_routine
    db 'TPDEVICE'
  end;
 
  const
    stDone = $100;
    stBusy = $200;
 
    cmInit = 0;
    cmInput= 4;
    cmInput_no_wait = 5;
    cmInput_status = 6;
    cmInput_flush = 7;
    cmOutput = 8;
    cmOutput_Verify = 9;
    cmOutput_status = 10;
    cmOutput_flush = 11;
 
  type
    request_header = record
      request_length : byte;
      subunit: byte;
      command_code : byte;
      status : word;
      reserved: array[1..8] of byte;
      case byte of
      cmInit : (num_units : byte;
                first_free : pointer;
                args : ^char;
                drive_num : byte;
               );
      cmInput :  { also used for output }
                 (media_descriptor : byte;
                 buffer : pointer;
                 byte_count : word);
      cmInput_no_wait : (next_char : char);
    end;
 
  var
    local_stack : array[1..4000] of byte;
    end_of_stack : byte;
    request : ^request_header;
    line : string;
 
  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, but remember:
     ****** The initialization code for SYSTEM and all other units hasn't
            ever been called!!  ******** }
  begin
    with request^ do
    begin
      case command_code of
      cmInit: begin
                first_free := ptr(dseg,ofs(saveint75)+4);{ Last thing in the
                                                           data segment in TP6}
                                                         { No heap!! }
                status := stDone;
                line := 'TPDRIVER successfully initialized.';
              end;
      cmInput: begin
                 if byte_count > length(line) then
                   byte_count := length(line);
                 move(line[1],buffer^,byte_count);
                 line := copy(line,byte_count+1,255);
                 status := stDone;
               end;
      cmInput_no_wait:
               begin
                 if length(line) > 0 then
                 begin
                   next_char := line[1];
                   status := stDone;
                 end
                 else
                   status := stBusy;
               end;
      cmInput_Status,cmOutput_Status,cmInput_Flush,cmOutput_Flush:
               status := stDone;
 
      cmOutput,cmOutput_Verify:
               begin
                 if byte_count + length(line) > 255 then
                   byte_count := 255 - length(line);
                 move(buffer^,line[length(line)+1],byte_count);
                 line[0] := char(byte(byte_count+length(line)));
                 status := stDone;
               end;
      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;
 
  begin
    writeln('TPDEVICE - DOS device driver written *entirely* in Turbo Pascal.');
    writeln('Install using DEVICE=TPDEV.EXE in CONFIG.SYS.');
    request := @header;  { Need a reference to pull in the header. }
  end.
 
 
--- Msg V3.2
@PATH: 221/177 12/12 13/13 124/4115 640/301 634/384
 * Origin: Murdoch's Point: Waterloo, Ontario, Canada (1:221/177.40)
 
I hope this has helped you.  I'm sure Duncan (who is a regular in this
newsgroup) will help you if you have problems.

Mitch.
Newsgroups: comp.lang.pascal