home *** CD-ROM | disk | FTP | other *** search
- {
- 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.