        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
