page 60,132
; SKELETON.ASM - A SKELETON DEVICE DRIVER V1.0
; (C) COPYRIGHT 1994 G.KLEIN
;
;This file contains structures, sample code and information intended
; to aid in building a device driver. It is set up as a skeleton, i.e.
; hardly any working code is provided. Only the Initialisation command
; is implemented - it displays a signon message and the commandline
; it was loaded from (excluding the "device[high]=" part). Sample code
; is provided both to let the driver load as to let it unload itself
; from memory after the initialisation command is processed. There is
; also a bit of code showing how to implement the driver as an EXE file,
; with different code depending on weather it is loaded from config.sys
; or from the command prompt.
;Although the major part of this file contains structures and other info,
; is assembles to a "working" device driver. It can be loaded from
; config.sys, and displays a signon message. When run from the commandline,
; it also displays a (different) message. It has been tested with and
; assembles "out of the box" under TASM V2.0 and MASM V5.0.
;The contents of this file is copyrighted. It may be used freely for
; whatever purpose you see it fit. Although a big effort has been made
; to ensure all information in this file is correct, no guarantees can
; be given. Any comments you may have on this file or the (correctness of)
; the information therein are welcome. Here is how you can reach me:
;
; Fidonet: Gertjan Klein @ 2:280/901.14
; Internet: gklein@hacktic.nl (Gertjan Klein)
; as of Sept. 1994: gklein@xs4all.nl (Gertjan Klein)
;####################################################################
;Some structures describing the request headers for the various commands.
; In the comment field, a "<" means this is input to the command, and ">"
; means this is output from the command. It is indicated weather the
; command applies to block (B) or character (C) device drivers, or both
; (B/C). If a command is defined later then version 2.0, the DOS version
; in which it first appears is placed in parenthesis. This also applies
; to the fields in the request headers.
rh0 struc ;0 - Initialisation (B/C)
rh0_len db ? ; < Length of packet
rh0_unit db ? ;Unused
rh0_cmd db 0 ; < Device driver command
rh0_stat dw ? ; > Status returned by device driver
rh0_rsvd db 8 dup (?) ;Reserved
rh0_units db ? ; > Number of units (B)
rh0_break dd ? ; < End of driver memory (5.0+)
; > Break address (new end drvr mem)
rh0_bpb dd ? ; < Pointer to cmdline (inc. name)
; > Pointer to BPB pointers array (B)
rh0_drv_ltr db ? ; < First available drive (B, 3.0+)
rh0_msgflag dw ? ; > 1 To make DOS show errors (5.0+)
rh0 ends
rh1 struc ;1 - Media Check (B)
rh1_len db ? ; < Length of packet
rh1_unit db ? ; < Unit code
rh1_cmd db 1 ; < Device driver command
rh1_stat dw ? ; > Status returned by device driver
rh1_rsvd db 8 dup (?) ;Reserved
rh1_media db ? ; < Current media descriptor
rh1_md_stat db ? ; > Media status (changed/not/?)
rh1_volID dd ? ; > Ptr to Volume ID (3.0+)
rh1 ends
rh2 struc ;2 - Get BPB (B)
rh2_len db ? ; < Length of packet
rh2_unit db ? ; < Unit code
rh2_cmd db 2 ; < Device driver command
rh2_stat dw ? ; > Status returned by device driver
rh2_rsvd db 8 dup (?) ;Reserved
rh2_media db ? ; < Current media descriptor
rh2_buff dd ? ; < Pointer to buffer
rh2_bpb dd ? ; > Pointer to BPB
rh2 ends
rh3 struc ;3 - IOCTL input (B/C)
rh3_len db ? ; < Length of packet
rh3_unit db ? ; < Unit code (B)
rh3_cmd db 3 ; < Device driver command
rh3_stat dw ? ; > Status returned by device driver
rh3_rsvd db 8 dup (?) ;Reserved
rh3_media db ? ;Unused
rh3_buff dd ? ; < Pointer to buffer
rh3_count dw ? ; < Transfer count (bytes)
; > Nr of bytes read
rh3 ends
rh4 struc ;4 - Input (B/C)
rh4_len db ? ; < Length of packet
rh4_unit db ? ; < Unit code (B)
rh4_cmd db 4 ; < Device driver command
rh4_stat dw ? ; > Status returned by device driver
rh4_rsvd db 8 dup (?) ;Reserved
rh4_media db ? ; < Media descriptor byte (B)
rh4_buff dd ? ; < Pointer to data transfer area
rh4_count dw ? ; < Transfer count (B:sectors,C:bytes)
; > Nr of sectors/bytes read
rh4_start dw ? ; < Start sector number (B)
rh4_volID dd ? ; > Pointer to volume ID (B, 5.0+)
rh4_HugeStart dd ? ; < 32-bits start sector (B, 5.0+)
; (if rh4_count = 0FFFFh)
rh4 ends
rh5 struc ;5 - Non-destructive input (C)
rh5_len db ? ; < Length of packet
rh5_unit db ? ;Unused
rh5_cmd db 5 ; < Device driver command
rh5_stat dw ? ; > Status returned by device driver
rh5_rsvd db 8 dup (?) ;Reserved
rh5_char db ? ; > Character returned
rh5 ends
rh6 struc ;6 - Input status (C)
rh6_len db ? ; < Length of packet
rh6_unit db ? ;Unused
rh6_cmd db 6 ; < Device driver command
rh6_stat dw ? ; > Status returned by device driver
rh6_rsvd db 8 dup (?) ;Reserved
rh6 ends
rh7 struc ;7 - Input flush (C)
rh7_len db ? ; < Length of packet
rh7_unit db ? ;Unused
rh7_cmd db 7 ; < Device driver command
rh7_stat dw ? ; > Status returned by device driver
rh7_rsvd db 8 dup (?) ;Reserved
rh7 ends
rh8 struc ;8 - Output (B/C)
rh8_len db ? ; < Length of packet
rh8_unit db ? ; < Unit code (B)
rh8_cmd db 8 ; < Device driver command
rh8_stat dw ? ; > Status returned by device driver
rh8_rsvd db 8 dup (?) ;Reserved
rh8_media db ? ; < Media descriptor byte (B)
rh8_buff dd ? ; < Pointer to data transfer area
rh8_count dw ? ; < Transfer count (B:sectors,C:bytes)
; > Nr of sectors/bytes read
rh8_start dw ? ; < Start sector number (B)
rh8_volID dd ? ; > Pointer to volume ID (B, 5.0+)
rh8_HugeStart dd ? ; < 32-bits start sector (B, 5.0+)
; (if rh8_count = 0FFFFh)
rh8 ends
rh9 struc ;9 - Output with verify (B/C)
rh9_len db ? ; < Length of packet
rh9_unit db ? ; < Unit code (B)
rh9_cmd db 9 ; < Device driver command
rh9_stat dw ? ; > Status returned by device driver
rh9_rsvd db 8 dup (?) ;Reserved
rh9_media db ? ; < Media descriptor byte (B)
rh9_buff dd ? ; < Pointer to data transfer area
rh9_count dw ? ; < Transfer count (B:sectors,C:bytes)
; > Nr of sectors/bytes read
rh9_start dw ? ; < Start sector number (B)
rh9_volID dd ? ; > Pointer to volume ID (B, 5.0+)
rh9_HugeStart dd ? ; < 32-bits start sector (B, 5.0+)
; (if rh9_count = 0FFFFh)
rh9 ends
rh0A struc ;0Ah - Output status (C)
rh0A_len db ? ; < Length of packet
rh0A_unit db ? ;Unused
rh0A_cmd db 0ah ; < Device driver command
rh0A_stat dw ? ; > Status returned by device driver
rh0A_rsvd db 8 dup (?) ;Reserved
rh0A ends
rh0B struc ;0Bh - Output flush (C)
rh0B_len db ? ; < Length of packet
rh0B_unit db ? ;Unused
rh0B_cmd db 0bh ; < Device driver command
rh0B_stat dw ? ; > Status returned by device driver
rh0B_rsvd db 8 dup (?) ;Reserved
rh0B ends
rh0C struc ;0Ch - IOCTL output (B/C)
rh0C_len db ? ; < Length of packet
rh0C_unit db ? ; < Unit code (B)
rh0C_cmd db 0ch ; < Device driver command
rh0C_stat dw ? ; > Status returned by device driver
rh0C_rsvd db 8 dup (?) ;Reserved
rh0C_media db ? ;Unused
rh0C_buff dd ? ; < Pointer to buffer
rh0C_count dw ? ; < Transfer count (bytes)
; > Nr of bytes written
rh0C ends
rh0D struc ;0Dh - Open (B/C) (3.0+)
rh0D_len db ? ; < Length of packet
rh0D_unit db ? ; < Unit code (B)
rh0D_cmd db 0dh ; < Device driver command
rh0D_stat dw ? ; > Status returned by device driver
rh0D_rsvd db 8 dup (?) ;Reserved
rh0D ends
rh0E struc ;0Eh - Close (B/C) (3.0+)
rh0E_len db ? ; < Length of packet
rh0E_unit db ? ; < Unit code (B)
rh0E_cmd db 0eh ; < Device driver command
rh0E_stat dw ? ; > Status returned by device driver
rh0E_rsvd db 8 dup (?) ;Reserved
rh0E ends
rh0F struc ;0Fh - Removable media (B) (3.0+)
rh0F_len db ? ; < Length of packet
rh0F_unit db ? ; < Unit code
rh0F_cmd db 0fh ; < Device driver command
rh0F_stat dw ? ; > Status returned by device driver
rh0F_rsvd db 8 dup (?) ;Reserved
rh0F ends
rh10 struc ;10h - Output until busy (C) (3.0+)
rh10_len db ? ; < Length of packet
rh10_unit db ? ;Unused
rh10_cmd db 10h ; < Device driver command
rh10_stat dw ? ; > Status returned by device driver
rh10_rsvd db 8 dup (?) ;Reserved
rh10_media db ? ;Unused
rh10_buff dd ? ; < Pointer to buffer
rh10_count dw ? ; < Transfer count (B:sectors,C:bytes)
; > Nr of sectors/bytes written
rh10 ends
;Commands 11h and 12h are undefined
rh13 struc ;13h - Generic IOCTL (3.2+:B, 3.3+:B/C)
rh13_len db ? ; < Length of packet
rh13_unit db ? ; < Unit code (B)
rh13_cmd db 13h ; < Device driver command
rh13_stat dw ? ; > Status returned by device driver
rh13_rsvd db 8 dup (?) ;Reserved
rh13_major db ? ; < Category
rh13_minor db ? ; < Minor function
rh13_SI dw ? ; < Contents of SI register
rh13_DI dw ? ; < Contents of DI register
rh13_pkt dd ? ; < Pointer to generic IOCTL req.
rh13 ends
;Commands 14 to 16 are undefined
rh17 struc ;17h - Get device (B) (3.2+)
rh17_len db ? ; < Length of packet
rh17_unit db ? ; < Unit number to check
; > Last active drive (or 0)
rh17_cmd db 17h ; < Device driver command
rh17_stat dw ? ; > Status returned by device driver
rh17_rsvd db 8 dup (?) ;Reserved
rh17 ends
rh18 struc ;18h - Set device (B) (3.2+)
rh18_len db ? ; < Length of packet
rh18_unit db ? ; < Unit to make active
rh18_cmd db 18h ; < Device driver command
rh18_stat dw ? ; > Status returned by device driver
rh18_rsvd db 8 dup (?) ;Reserved
rh18 ends
rh19 struc ;19h - IOCTL query (B/C) (5.0+)
rh19_len db ? ; < Length of packet
rh19_unit db ? ; < Unit code (B)
rh19_cmd db 19h ; < Device driver command
rh19_stat dw ? ; > Status returned by device driver
rh19_rsvd db 8 dup (?) ;Reserved
rh19_major db ? ; < Category
rh19_minor db ? ; < Minor function
rh19_SI dw ? ; < Contents of SI register
rh19_DI dw ? ; < Contents of DI register
rh19_pkt dd ? ; < Pointer to IOCTL query req.
rh19 ends
;####################################################################
;General equates:
bptr equ byte ptr
wptr equ word ptr
dptr equ dword ptr
os equ offset
;####################################################################
cseg SEGMENT PARA PUBLIC 'CODE'
ASSUME cs:cseg
nxt_dev dd -1 ;Pointer to next device driver in chain
attr dw 8000h ;Attribute: character device
strat dw strategy ;Address of strategy routine
intr dw interrupt ;Address of interrupt routine
dname db "NOTELEKS" ;Name of the device driver (C), or
; (1 byte:) number of devices (B)
;The attribute word is defined as follows:
; Bit 0: (C): 1 if driver is stdin
; 1: (C): 1 if driver is stdout
; (B): 1 if driver supports 32-bit sector addressing
; (commands 4, 8 and 9)
; 2: (C): 1 if driver is NUL device (only allowed for DOS's own driver)
; 3: (C): 1 if driver is clock device
; 4: (C): 1 if driver supports fast character I/O (int 29h)
; 5: undefined
; 6: (C/B): 0 if driver supports commands 17h and 18h and/or 13h
; (get/set logical drive and generic IOCTL)
; 7: (C/B): 1 if driver supports IOCTL query (command 19h)
; 8: undefined
; 9: undefined
; 10: undefined
; 11: (C/B): 1 if driver supports commands 0dh, 0eh and 0fh
; (Open/close device and removable media)
; 12: (C): 1 if driver supports output until busy (command 10h)
; (B): 1 if driver requires first FAT sector for build BPB
; (command 2)
; 13: undefined
; 14: (C/B): 1 if driver supports IOCTL read and write (commands 3
; and 0ch)
; 15: (C): 1 if driver supports a character device
; (B): 0 if driver supports a block device
ReqHeader label dword
RhOffs dw ? ;Saved offset of request header
RhSeg dw ? ;Saved segment of request header
strategy:
mov RhSeg,es ;Save request header segment
mov RhOffs,bx ;Save request header offset
retf ;Return to caller
;The following command table is initialised to the exit routines to
; use when the command is not implemented. In this skeleton, only Init
; is implemented. Change the pointers as you add routines.
;Normally, at least the following routines are required:
; For character device drivers: 0,4,5,6,7,8,9,0ah,0bh
; For block devices : 0,1,2,4,8,9
cmdtab dw Init ; 0 Initialisation command
dw Exit_Done ; 1 Media_check
dw Exit_Done ; 2 Build BPB
dw Exit_UC ; 3 IOCtl in
dw Exit_Done ; 4 Input
dw Exit_Busy ; 5 Non-destructive input
dw Exit_Done ; 6 Input status
dw Exit_Done ; 7 Input flush
dw Exit_Done ; 8 Output
dw Exit_Done ; 9 Output with verify
dw Exit_Done ;0Ah Output status
dw Exit_Done ;0Bh Output flush
dw Exit_UC ;0Ch IOCtl out
dw Exit_Done ;0Dh Device open (3.0+)
dw Exit_Done ;0Eh Device close (3.0+)
dw Exit_UC ;0Fh Removable media (3.0+)
dw Exit_UC ;10h Output till busy (3.0+)
dw Exit_UC ;11h (Unused)
dw Exit_UC ;12h (Unused)
dw Exit_UC ;13h Generic IOCTL (3.2:B, 3.3+:B/C)
dw Exit_UC ;14h (Unused)
dw Exit_UC ;15h (Unused)
dw Exit_UC ;16h (Unused)
dw Exit_UC ;17h Get logical device (3.2+)
dw Exit_UC ;18h Set logical device (3.2+)
dw Exit_UC ;19h IOCTL query (5.0+)
interrupt:
push ax ;Save general purpose registers
push bx
push cx
push dx
push si
push di
push ds ;Save segment registers
push es
mov ax,cs ;Make DS point to us
mov ds,ax
ASSUME cs:cseg,ds:cseg
les bx,[ReqHeader] ;Get ptr to request header in ES:BX
mov al,es:[bx.rh0_cmd] ;Get command
cmp al,19h ;Check if within range
ja Exit_UC ; no: set unknown command bit and exit
shl al,1 ; yes: make word index
xor ah,ah
mov di,ax ;Place it in index register
jmp [cmdtab.di] ; and jump to apropriate routine
Exit_Busy:
or es:[bx.rh0_stat],200h
jmp short Exit_Done
Exit_UC:
or es:[bx.rh0_stat],8003h ;Set error and unknown command bits
Exit_Done:
or es:[bx.rh0_stat],100h ;Set done bit
exit:
pop es ;Restore segment registers
pop ds
pop di ;Restore general purpose registers
pop si
pop dx
pop cx
pop bx
pop ax
retf ;Return to caller
;********************************************************************
;This is a good place to insert routines and data that should stay resident.
; Anything that can be disposed of after initialisation should be placed
; after the break address, that is, after the Init routine.
;********************************************************************
Init:
;Display signon message:
mov ah,3 ;Read cursor position
xor bh,bh ; for video page 0
int 10h ;(BIOS) (Returned in dx)
mov ax,1301h ;Write string in teletype mode
mov bl,3 ; with attribute cyan
mov cx,so_len ; length of string
mov bp,offset signon ; start of string
push cs ; in es:bp
pop es
int 10h ;(BIOS)
;Show commandline (including driver name):
mov ah,3 ;Read cursor position
xor bh,bh ; for video page 0
int 10h ;(BIOS) (Returned in dx)
les bx,[ReqHeader] ;Get ptr to request header in ES:BX
les bp,es:[bx.rh0_bpb] ;Get pointer to commandline in ES:BP
mov di,bp ;Get pointer in ES:DI too
mov al,0ah ;Terminating character of commandline
mov cx,256 ;Search max 256 bytes
repnz scasb ;Search for it
mov cx,di ;Get pointer to end of string
sub cx,bp ;Calculate length
mov ax,1301h ;Write string in teletype mode
mov bx,2 ; with attribute green
int 10h ;(BIOS)
les bx,[ReqHeader] ;Get ptr to request header back
DoNotInstall:
mov wptr es:[bx.rh0_break],0 ;Set zero break address
mov wptr es:[bx.rh0_break+2],cs ;Set break address segment
jmp Exit_Done
Install:
mov wptr es:[bx.rh0_break],os Init ;Set break address to Init
mov wptr es:[bx.rh0_break+2],cs ;Set break address segment
jmp Exit_Done
;********************************************************************
EXE:
;This is what gets executed when the device driver is ran as an EXE file.
; It can be safely deleted if the device driver is converted to a binary
; file (.SYS or .BIN), though the label in the END directive should be
; changed then. This code could be used to communicate with the resident
; device driver.
mov ax,cs ;Make DS=CS
mov ds,ax
mov ah,9 ;DOS display string function
mov dx,os emsg1 ;Message to display
int 21h ;(DOS)
mov ax,4c01h ;Back to DOS with errorlevel 1
int 21h ;(DOS)
;********************************************************************
signon db 13,10,"SKELETON - Skeleton device driver (C) 1994 G.Klein."
db 13,10,"Commandline: "
so_len equ $-signon
emsg1 db "SKELETON - This program must be installed from config.sys."
db 13,10,'$'
cseg ENDS
END EXE