Path: utzoo!utgpu!news-server.csri.toronto.edu!rpi!usc!apple!portal!gupta!few From: few@gupta.portal.com (Frank Whaley) Newsgroups: comp.os.msdos.programmer Subject: Device Drivers in C Message-ID: <1991Apr4.160734.6391@gupta.portal.com> Date: 4 Apr 91 16:07:34 GMT Organization: Gupta Technologies Inc Lines: 581 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