[Back to MISC SWAG index] [Back to Main SWAG index] [Original]

    {
    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.
    }
    { 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; assembler;
    { Here's the trick:  an assembler routine in the main program, guaranteed to
      be linked first in the .EXE file!!}
    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
            { Last thing in the data segment in TP6 - No heap!!}
            first_free := ptr(dseg, ofs(saveint75) + 4);
            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.

[Back to MISC SWAG index] [Back to Main SWAG index] [Original]