[comp.os.msdos.programmer] Device drivers in C

few@gupta.portal.com (Frank Whaley) (04/05/91)

The attached shar file comprises a demonstration of building MS-DOS
Installable Device Drivers with C (currently Turbo C and Microsoft C).

I once had a much larger package designed for the days of crude
compilers.  Things have improved so I have constructed a new package
that does more in less space.

Frank Whaley
Software Engineer
Gupta Technologies
few@gupta.com

Water separates the people of the world;
Wine unites them.
-----
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	mono.c
#	idd.h
#	iddstart.asm
#	tc.bat
#	msc.bat
# This archive created: Thu Apr  4 07:55:21 1991
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'mono.c'
then
	echo shar: "will not over-write existing file 'mono.c'"
else
sed 's/^X//' << \SHAR_EOF > 'mono.c'
X/*
X *	mono.c - monochrome screen device driver
X *
X *	This code provides a device driver for a device named "MON"
X *	which can be opened and written to like any character device.
X *	Output is directed to the monochrome display.  This device
X *	was once handy for debugging a graphics program.  Now it is
X *	just an example of how to build Installable Device Drivers
X *	with C.  For practice, try expanding MON into CON, and provide
X *	a replacement for ANSI.SYS.
X *
X *	Note that drivers that provide interrupt handlers must be built
X *	with Turbo C in the 'tiny' (-mt) model.  The -mt switch causes
X *	interrupt functions to use the code segment variable DGROUP@
X *	for finding the local data segment, instead of making a reference
X *	to the group DGROUP which requires a fixup.
X */
X
X#include "idd.h"
X
X#define VidSeg	(word far *)0xB0000000	/*  addr of monochrome video RAM  */
X#define Attr	0x0700			/*  video attribute mask  */
X#define Blank	(Attr + ' ')		/*  a blank  */
X
Xint row;		/*  current row number  */
Xint col;		/*  current column number  */
X
Xword far *VidAddr;	/*  -> current display character  */
X
X	/*  forward declarations  */
Xvoid cls(void);
Xvoid output(Request far *req);
Xvoid putone(char c);
Xvoid scrollUp(void);
Xvoid wordCopy(word far *from, word far *to, int len);
Xvoid wordSet(word far *ptr, int len, word value);
X
X/*
X *	IDDmain - Installable Device Driver entry point
X */
Xvoid
XIDDmain(req, end)
XRequest far *req;	/*  -> request header  */
Xdword end;		/*  -> end of driver memory  */
X{
X	/*  switch on command code  */
X	switch ( req->commandCode )
X	{
X	case INIT:
X		cls();
X		((InitParms far *)req)->endAddr = end;
X		req->status = Done;
X		break;
X
X	case OUTPUT:
X	case OUTVERIFY:
X		output(req);
X		/*FALLTHRU*/
X	case OUTSTATUS:
X	case OUTFLUSH:
X		req->status = Done;
X		break;
X
X	default:	/*  oops  */
X		req->status = (Error + Done + UnknownCommand);
X		break;
X	}
X}
X
X/*
X *	output - handle output request
X */
Xvoid
Xoutput(Request far *req)
X{
X	InOutParms far *iop = (InOutParms far *)req;
X	byte far *ta;	/*  -> transfer addr  */
X	int ctr;	/*  byte count  */
X	char c;
X
X	ta = (byte far *)iop->transfer;
X	ctr = iop->count;
X
X	while ( ctr-- )
X		switch ( c = *ta++ )
X		{
X		case '\r' :
X			VidAddr -= col;
X			col = 0;
X			break;
X
X		case '\n' :
X			VidAddr += 80;
X			if ( ++row > 24 )
X				scrollUp();
X			break;
X
X		case '\b' :
X			if ( col )
X			{
X				col--;
X				VidAddr--;
X			}
X			break;
X
X		case '\t' :
X			do
X				putone(' ');
X			while ( col & 7 );
X			break;
X
X		case 0x1A :
X			cls();
X			break;
X
X		default :
X			putone(c);
X			break;
X		}
X}
X
X/*
X *	cls - clear the screen
X */
Xvoid
Xcls()
X{
X	/*  fill the screen with blanks  */
X	wordSet(VidSeg, 2000, Blank);
X
X	row = col = 0;
X	VidAddr = VidSeg;
X}
X
X/*
X *	scrollUp - scroll screen up one line
X */
Xvoid
XscrollUp()
X{
X	/*  move up  */
X	wordCopy(VidSeg + 80, VidSeg, 1920);
X	/*  clear last line  */
X	wordSet(VidSeg + 1920, 80, Blank);
X
X	VidAddr -= 80;
X	row = 24;
X}
X
X/*
X *	putone - display one character, scrolling at line-wrap
X */
Xvoid
Xputone(char c)
X{
X	*VidAddr++ = Attr + c;
X
X	if ( ++col > 79 )
X	{
X		VidAddr += (80 - col);
X		col = 0;
X		if ( ++row > 24 )
X			scrollUp();
X	}
X}
X
X/*
X *	wordCopy - copy a range of words
X */
Xvoid
XwordCopy(word far *from, word far *to, int len)
X{
X	while ( len-- )
X		*to++ = *from++;
X}
X
X/*
X *	wordSet - set a range of words
X */
Xvoid
XwordSet(word far *ptr, int len, word value)
X{
X	while ( len-- )
X		*ptr++ = value;
X}
X
X/*  END of mono.c  */
SHAR_EOF
fi
if test -f 'idd.h'
then
	echo shar: "will not over-write existing file 'idd.h'"
else
sed 's/^X//' << \SHAR_EOF > 'idd.h'
X/*
X *	idd.h - Installable Device Driver header
X */
X
X	/*  magic Intel types  */
Xtypedef unsigned char	byte;
Xtypedef unsigned int	word;
Xtypedef void far *	dword;
X
X	/*  status word bits  */
X#define	Error	0x8000
X#define	Busy	0x0100
X#define	Done	0x0080
X
X	/*  media descriptor byte bits  */
X#define	TwoSided	1
X#define	EightSector	2
X#define	Removable	4
X
X	/*  error return codes  */
X#define	WriteProtect	0
X#define	UnknownUnit	1
X#define	DeviceNotReady	2
X#define	UnknownCommand	3
X#define	CRCError	4
X#define	BadLength	5
X#define	SeekError	6
X#define	UnknownMedia	7
X#define	SectorNotFound	8
X#define	NoPaper		9
X#define	WriteFault	10
X#define	ReadFault	11
X#define	GeneralFailure	12
X
X	/*  function numbers  */
X#define INIT		0
X#define MEDIACHECK	1
X#define BUILDBPB	2
X#define IOCTLIN		3
X#define INPUT		4
X#define NDINPUT		5
X#define INPUTSTATUS	6
X#define INPUTFLUSH	7
X#define OUTPUT		8
X#define OUTVERIFY	9
X#define OUTSTATUS	10
X#define OUTFLUSH	11
X#define IOCTLOUT	12
X#define DEVOPEN		13
X#define DEVCLOSE	14
X#define REMMEDIA	15
X
X	/*  structures  */
X
Xtypedef struct
X{
X	byte length;
X	byte unitCode;
X	byte commandCode;
X	word status;
X	byte reserved[8];
X} Request;
X
Xtypedef struct
X{
X	Request reqHdr;		/*  Request Header  */
X	byte nUnits;		/*  number of units  */
X	dword endAddr;		/*  Ending Address  */
X	dword bpbArray;		/*  ptr to BPB array  */
X} InitParms;
X
Xtypedef struct
X{
X	Request reqHdr;		/*  Request Header  */
X	byte mediaDesc;		/*  Media Descriptor  */
X	byte returnCode;	/*  Return Code  */
X} MediaParms;
X
Xtypedef struct
X{
X	Request reqHdr;		/*  Request Header  */
X	byte mediaDesc;		/*  Media Descriptor  */
X	dword transfer;		/*  Transfer Address  */
X	dword bpbTable;		/*  ptr to BPB table  */
X} BPBParms;
X
Xtypedef struct
X{
X	Request reqHdr;		/*  Request Header  */
X	byte mediaDesc;		/*  Media Descriptor  */
X	dword transfer;		/*  Transfer Address  */
X	word count;		/*  Byte/Sector Count  */
X	word start;		/*  Starting Sector Number  */
X} InOutParms;
X
Xtypedef struct
X{
X	Request reqHdr;		/*  Request Header  */
X	byte Byte;		/*  Byte Read From Device  */
X} ndInputParms;
X
Xtypedef struct
X{
X	Request reqHdr;
X} StatusParms;
X
Xtypedef struct
X{
X	Request reqHdr;
X} FlushParms;
X
Xtypedef struct
X{
X	word BytesPerSector;
X	byte SecsPerAllocUnit;
X	word ReservedSectors;
X	byte FATCount;
X	word RootDirEntries;
X	word SectorsPerLogical;
X	byte MediaDesc;
X	word SecsPerFAT;
X} BPB;
X
Xtypedef struct
X{
X	byte BootJump[3];
X	byte Name[8];
X	BPB BootBPB;
X	word SecsPerTrack;
X	word HeadCount;
X	word HiddenCount;
X} BootSector;
X
X/*  END of idd.h  */
SHAR_EOF
fi
if test -f 'iddstart.asm'
then
	echo shar: "will not over-write existing file 'iddstart.asm'"
else
sed 's/^X//' << \SHAR_EOF > 'iddstart.asm'
X	PAGE	60, 132
XTITLE	IDDStart - Installable Device Driver Header
X
X;-----------------------------------------------------------------------|
X;	Sample Installable Device Driver Header				|
X;-----------------------------------------------------------------------|
X
X;  Segment declarations
XIGROUP	Group	_TEXT, TAIL
XDGROUP	Group	_DATA, CONST, c_common, _BSS, DTAIL
X
X_TEXT	Segment Para Public 'CODE'
X	Assume	CS:_TEXT
X_TEXT	EndS
XTAIL	Segment 'CODE'
XTAIL	EndS
X_DATA	Segment Para Public 'DATA'
X	Assume	DS:_DATA
X_DATA	EndS
XCONST	Segment Byte Public 'CONST'
XCONST	EndS
Xc_common Segment Byte Public 'BSS'
Xc_common EndS
X
X_TEXT	Segment
X
X;-----------------------------------------------------------------------|
X;	External declarations of driver functions			|
X;-----------------------------------------------------------------------|
X
X	Extrn	_IDDmain:Near
X
X	;  where do we begin...
X	ORG	0
X
XIDDStart	Proc	Far
X
X;-----------------------------------------------------------------------|
X;	Device Header							|
X;-----------------------------------------------------------------------|
X
X	DD	-1			;  -> next device (init to -1)
X; CONFIGURE: the following value must be set specifically for your driver
X	DW	8000H			;  character only
X	DW	Strategy		;  -> device strategy
X	DW	Interrupt		;  -> device interrupt
X; CONFIGURE: the following value must be set specifically for your driver
X	DB	"MON     "		;  driver name
X
X;-----------------------------------------------------------------------|
X;	Code Segment Variables						|
X;-----------------------------------------------------------------------|
X	Public	DGROUP@
XDGROUP@	DW	0			;  our data segment
Xrequest	DD	(?)			;  copy of request hdr ptr
XssEntry	DW	(?)			;  entry SS
XspEntry	DW	(?)			;  entry SP
X
X	PAGE
X;-----------------------------------------------------------------------|
X;	Device Strategy							|
X;									|
X;	ENTRY :	ES:BX -> Request Header					|
X;									|
X;	EXIT :	all registers preserved					|
X;									|
X;-----------------------------------------------------------------------|
X
XStrategy:
X	;  save request hdr ptr
X	MOV	Word Ptr CS:request,BX
X	MOV	Word Ptr CS:request + 2,ES
X	RET
X
X	PAGE
X;-----------------------------------------------------------------------|
X;	Device Interrupt						|
X;									|
X;	ENTRY :	anything						|
X;									|
X;	EXIT :	all registers preserved					|
X;									|
X;-----------------------------------------------------------------------|
X
XInterrupt:
X	;  save the world
X	PUSH	DS			;  (+1)
X	PUSH	ES			;  (+2)
X	PUSH	AX			;  (+3)
X	PUSH	BX			;  (+4)
X	PUSH	CX			;  (+5)
X	PUSH	DX			;  (+6)
X	PUSH	SI			;  (+7)
X	PUSH	DI			;  (+8)
X	PUSH	BP			;  (+9)
X
X	MOV	CS:ssEntry,SS		;  save entry SS
X	MOV	CS:spEntry,SP		;  and SP
X
X	;  one-time compute of DS
X	MOV	AX,CS:DGROUP@
X	TEST	AX,AX
X	JNE	haveDS
X
X	MOV	AX,Offset IGROUP:TAIL	;  compute our DS
X
X	MOV	CL,4
X	SHR	AX,CL
X	MOV	CX,CS
X	ADD	AX,CX
X	MOV	CS:DGROUP@,AX
X
XhaveDS:
X	MOV	BX,Offset DGROUP:locStk	;  set our DS, SS, BP, and SP
X	MOV	DS,AX
X	;CLI				;  very old chips need this
X	MOV	SS,AX
X	MOV	SP,BX
X	;STI
X	MOV	BP,BX
X
X	;  call IDDmain(Request far *req, void far *end)
X	PUSH	DS
X	MOV	AX,Offset DGROUP:DTAIL
X	PUSH	AX
X	PUSH	Word Ptr CS:request + 2
X	PUSH	Word Ptr CS:request
X	CALL	_IDDmain
X
X	SUB	SP,8
X
X	;  restore entry registers
X	MOV	SS,CS:ssEntry
X	MOV	SP,CS:spEntry
X	POP	BP			;  (+8)
X	POP	DI			;  (+7)
X	POP	SI			;  (+6)
X	POP	DX			;  (+5)
X	POP	CX			;  (+4)
X	POP	BX			;  (+3)
X	POP	AX			;  (+2)
X	POP	ES			;  (+1)
X	POP	DS			;  (+0)
X	RET
X
XIDDStart	EndP
X
X_TEXT	EndS
X
X	PAGE
X;-----------------------------------------------------------------------|
X;	Data Segment							|
X;-----------------------------------------------------------------------|
X
X_DATA	Segment
X
X	Public	__acrtused
X__acrtused	EQU	9876H
X
X_DATA	EndS
X
X_BSS	Segment Word Public 'BSS'
X	DB	2048 Dup (?)		;  our stack
XlocStk	Label	Word
X_BSS	EndS
X
XDTAIL	Segment Word 'BSS'
XDTAIL	EndS
X
X	END	IDDStart		;  of IDDStart.asm
SHAR_EOF
fi
if test -f 'tc.bat'
then
	echo shar: "will not over-write existing file 'tc.bat'"
else
sed 's/^X//' << \SHAR_EOF > 'tc.bat'
X: TC.BAT - build MONO.SYS with Turbo C 2.0
X:  Note that TASM can't seem to build a version of iddstart.obj that
X:  doesn't require fixups.  We may be doing an Evil Thing, but this
X:  is MS-DOS.
Xmasm /mx/t iddstart;
Xtcc -c mono.c
Xtlink iddstart+mono,mono/x/c
Xdel iddstart.obj
Xdel mono.obj
Xexe2bin mono.exe mono.sys
Xdel mono.exe
SHAR_EOF
chmod +x 'tc.bat'
fi
if test -f 'msc.bat'
then
	echo shar: "will not over-write existing file 'msc.bat'"
else
sed 's/^X//' << \SHAR_EOF > 'msc.bat'
X: MSC.BAT - build MONO.SYS with Microsoft C (5.1 or 6.0)
Xmasm /mx/t iddstart;
Xcl -Gs -Zp -c mono.c
Xlink iddstart+mono,mono;
Xdel iddstart.obj
Xdel mono.obj
Xexe2bin mono.exe mono.sys
Xdel mono.exe
SHAR_EOF
chmod +x 'msc.bat'
fi
exit 0
#	End of shell archive
-- 
Frank Whaley
Software Engineer
Gupta Technologies
few@gupta.com

tommyp@ida.liu.se (Tommy Pedersen) (04/16/91)

Sorry, this is not an additional comment!

But I saw this issue being brought up by
	 Kraig Eno and
	 Frank Whaley
in the Expired list on my node, so if any of you two guys would like
to email me your contributions you would make me very happy. And please
if someone else has saved these, please send them to me.

Thanks in advance,

/Tommy Pedersen
 ________________________________________________________________
|E-mail: tommyp@isy.liu.se       ||    Telephone: +46 13 282369  |
|S-mail: Tommy Pedersen          ||          FAX: +46 13 289282  |
|        Dept. of EE             ||______________________________|
|        Linkoping University    ||                              |
|        S-581 83 Linkoping      ||                              |
|        SWEDEN                  ||                              |
|________________________________||______________________________|