home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Languages / Masm V6.11 / SAMPLES / DOSDEV / ATOMS.AS$ / ATOMS
Encoding:
Text File  |  1992-11-12  |  32.1 KB  |  726 lines

  1. ;----------------------------------------------------------------------------;
  2. ; ATOMS.ASM:    A simple installable character device driver
  3. ;----------------------------------------------------------------------------;
  4. ; based on skeleton in The MS-DOS Encyclopedia, (c) 1988 Microsoft Press
  5. ;----------------------------------------------------------------------------;
  6.  
  7.     include dos.inc         ; macros to @ShowStr, etc...
  8.     
  9.     .model  tiny, c
  10.     .386
  11.     
  12. ;----------------------------------------------------------------------------;
  13. ;                           Typedefs & Prototypes 
  14. ;----------------------------------------------------------------------------;
  15.  
  16. NPBYTE  TYPEDEF NEAR PTR BYTE           ; near pointer to a byte
  17. FPTR    TYPEDEF FAR PTR                 ; far pointer
  18. NPTR    TYPEDEF NEAR PTR                ; near pointer
  19. SEGPTR  TYPEDEF WORD                    ; segment selector
  20. DEVLINK TYPEDEF SDWORD                  ; link to other devices
  21. ATRIBUT TYPEDEF WORD                    ; device attribute word
  22.  
  23. st_len  PROTO NEAR, :NPBYTE, :SEGPTR    ; gets the length of string into ax
  24. int2hex PROTO NEAR, :WORD, :NPBYTE      ; converts integers to hexadecimal
  25. scan    PROTO NEAR, :NPBYTE, :SEGPTR    ; scans the command line
  26.  
  27. ;----------------------------------------------------------------------------;
  28. ;                          Flags & Numeric Equates                
  29. ;----------------------------------------------------------------------------;
  30.  
  31. MaxCmd  EQU     24t             ; max command code for DOS 3.2+
  32. cr      EQU     0Dh             ; carriage return
  33. lf      EQU     0Ah             ; linefeed
  34. eom     EQU     '$'             ; end-of-message signal
  35. space   EQU     20h             ; ascii space
  36. tab     EQU     09h             ; ascii tab
  37.  
  38. ; definition of a Request Header structure so that we can access its elements. 
  39. ; this structure is not exhaustive at all: block device use some addresses for
  40. ; different purposes, and have data beyond the cmd_seg word.
  41.  
  42. REQ_HEADER      STRUCT 1t
  43.     lengt   BYTE    ?               ; request header length
  44.     unit    BYTE    ?               ; unit number (for Block Devs. only)
  45.     code    BYTE    ?               ; command code
  46.     stat    WORD    ?               ; status word
  47.     reserv  QWORD   ?               ; reserved for DOS us
  48.     media   BYTE    ?               ; Media ID    (for Block Devs. only)
  49.     xfer    NPTR    ?               ; offset of data buffer
  50.     xseg    SEGPTR  ?               ; segment of data buffer
  51.     UNION
  52.         xcount  WORD    ?       ; count of bytes in request, or
  53.         cmd_off NPTR    ?       ; offset of CONFIG.SYS line
  54.     ENDS
  55.     cmd_seg SEGPTR  ?               ; segment of CONFIG.SYS line
  56. REQ_HEADER      ENDS
  57.  
  58. ; values for the different 'magic numbers' used with Device Drivers
  59.  
  60. fNEXTLINK   EQU -1              ; word to tell DOS to substitute by address of
  61.                 ; next device
  62. fCHARDEVICE EQU 8000h           ; bit to define device as a character device
  63. fOPENCLOSE  EQU 0040h           ; bit to indicate that device supports open/close
  64. fDONE       EQU 0100h           ; Status Done bit meaning device is done
  65. fERROR      EQU 8000h           ; Status Error bit meaning error on operation
  66. fWRITE_E    EQU 000Ah           ; Write Fault Error bit
  67. fUNKNOWN_E  EQU 0003h           ; Unknown Command Error bit
  68.  
  69. ; values for the search flags used by Write Routine
  70.  
  71. fDELETE EQU     2
  72. fINSERT EQU     4
  73. fSEARCH EQU     8
  74.  
  75. ; value for buffer (atom memory) limits
  76.  
  77. BUFMINIMUM EQU  00400h  ; the minimum will be 1024 bytes
  78. BUFMAXIMUM EQU  07FFFh  ; the maximum will be 32K. It can be less than that,
  79.             ; but not greater, because of the signed index for 
  80.             ; scasb operations.
  81.  
  82. ;----------------------------------------------------------------------------;
  83. ;                             Code Segment                                   
  84. ;----------------------------------------------------------------------------;
  85.     .code
  86.     
  87.     org     0               ; drivers should start at address 0000h
  88.                             ; this will cause a linker warning - ignore it.
  89.  
  90. Header:                         ; device driver header
  91.     DEVLINK  fNEXTLINK       ; link to next device driver
  92.     ATRIBUT fCHARDEVICE+fOPENCLOSE  ; device attribute word: 
  93.                     ; char.device+open/close
  94.     NPTR    Strat           ; 'Strat' entry point
  95.     NPTR    Intr            ; 'Intr' entry point
  96.     BYTE    'ATMS     '     ; logical device name (needs to be 8 chars)
  97.  
  98. ;----------------------------------------------------------------------------;
  99. ; data variables
  100. ;----------------------------------------------------------------------------;
  101.  
  102. null     BYTE    0              ; dummy to do a quick erase of pAtomValue
  103. pAtomVal NPBYTE  null           ; pointer to value of result of atom search
  104. MemEnd   NPBYTE  AtomList       ; end of used memory:      initially AtomList
  105. MaxMem   NPBYTE  AtomList       ; end of available memory: initially AtomList
  106. lpHeader FPTR    0              ; far pointer to request header
  107.         
  108. ;----------------------------------------------------------------------------;
  109. ; Dispatch table for the interrupt routine command codes                     
  110. ;----------------------------------------------------------------------------;
  111.  
  112. Dispatch:                       
  113.     NPTR    Init            ;  0 = init driver 
  114.     NPTR    Error           ;  1 = Media Check         (block devices only) 
  115.     NPTR    Error           ;  2 = build BPB           (block devices only)
  116.     NPTR    Error           ;  3 = I/O control read         (not supported)
  117.     NPTR    Read            ;  4 = read (input) from device  (int 21h, 3Fh)
  118.     NPTR    Error           ;  5 = nondestructive read      (not supported)  
  119.     NPTR    Success         ;  6 = ret input status        (int 21h, 4406h) 
  120.     NPTR    Error           ;  7 = flush device input buffer (not supportd)         
  121.     NPTR    Write           ;  8 = write (output) to device  (int 21h, 40h)
  122.     NPTR    Write           ;  9 = write with verify (== write)  (21h, 40h)
  123.     NPTR    OutStat         ; 10 = ret output status       (int 21h, 4407h)
  124.     NPTR    Error           ; 11 = flush output buffer      (not supported) 
  125.     NPTR    Error           ; 12 = I/O control write        (not supported)
  126.     NPTR    Success         ; 13 = device open               (int 21h, 3Dh)
  127.     NPTR    Success         ; 14 = device close              (int 21h, 3Eh)
  128.     NPTR    Error           ; 15 = removable media     (block devices only)
  129.     NPTR    Error           ; 16 = Output until Busy   (mostly for spooler)
  130.     NPTR    Error           ; 17 = not used
  131.     NPTR    Error           ; 18 = not used
  132.     NPTR    Error           ; 19 = generic IOCTL            (not supported)
  133.     NPTR    Error           ; 20 = not used
  134.     NPTR    Error           ; 21 = not used
  135.     NPTR    Error           ; 22 = not used
  136.     NPTR    Error           ; 23 = get logical device  (block devices only)
  137.     NPTR    Error           ; 24 = set logical device  (block devices only)
  138.  
  139. ;----------------------------------------------------------------------------;
  140. ; Strategy Routine
  141. ;----------------------------------------------------------------------------;
  142. ; device driver Strategy routine, called by MS-DOS kernel with
  143. ; ES:BX = address of request header
  144. ;----------------------------------------------------------------------------;
  145.  
  146. Strat   PROC FAR
  147.     mov     word ptr cs:[lpHeader], bx      ; save the address of the 
  148.     mov     word ptr cs:[lpHeader+2], es    ; request into 'lpHeader', and                  
  149.     ret                                     ; back to MS-DOS kernel
  150. Strat   ENDP
  151.  
  152. ;----------------------------------------------------------------------------;
  153. ; Intr
  154. ;----------------------------------------------------------------------------;
  155. ; Device driver interrupt routine, called by MS-DOS kernel after call to     
  156. ; Strategy routine                                                           
  157. ; This routine basically calls the appropiate driver routine to handle the
  158. ; requested function. 
  159. ; Routines called by Intr expect:
  160. ;       ES:DI   will have the address of the request header
  161. ;       DS      will be set to cs
  162. ; These routines should only affect ax, saving es,di,ds at least
  163. ;
  164. ; Input: NONE   Output: NONE   -- data is transferred through request header
  165. ;
  166. ;----------------------------------------------------------------------------;
  167.  
  168. Intr    PROC FAR
  169.     pusha                   ; save registers
  170.     pushf                   ; save flags
  171.     cld                     ; direction flag: go from low to high address
  172.                 
  173.     mov     si, cs          ; make local data addressable
  174.     mov     ds, si          ; by setting ds = cs
  175.             
  176.     les     di, [lpHeader]  ; ES:DI = address of req.header
  177.  
  178.     ASSUME  di:PTR REQ_HEADER ; to be able to use the REQ_HEADER offsets
  179.  
  180.     xor     ebx, ebx        ; erase ebx
  181.     mov     bl,es:[di].code ; get EBX = command code (from req.header)
  182.     
  183.     .IF (bx > MaxCmd)                 ; check to make sure we have a valid
  184.         call    Error             ; command code
  185.     .ELSE                             ; else, call command-code routine,  
  186.         call    NPTR PTR Dispatch[2*ebx] ; indexed from Dispatch table
  187.     .ENDIF                            ; (Ebx used to allow scaling factors)
  188.  
  189.     or      ax, fDONE       ; merge Done bit into status and
  190.     mov     es:[di].stat,ax ; store status into request header
  191.     
  192.     ASSUME  di:NOTHING      ; di will be something else now
  193.  
  194.     popf                    ; restore registers
  195.     popa                    ; restore flags
  196.     ret                     ; return to MS-DOS kernel
  197. Intr    ENDP
  198.  
  199. ; Command-code routines are called by the Interrupt routine via the dispatch
  200. ; table. Each routine should return AX = 0000H if function was completed
  201. ; successfully or AX = 8000h + error code if function failed.
  202.  
  203. ;----------------------------------------------------------------------------;
  204. ; function 4 = read (input) from device                                      
  205. ;----------------------------------------------------------------------------;
  206. ; what the function basically does is transfer from the string that pAtomVal 
  207. ; points, to the buffer pointed to in the request header. the values have 
  208. ; to be loaded into ES:DI and DS:SI for the movsb instruction, and other
  209. ; checks are made to account for differences in sizes                                ; 
  210. ;----------------------------------------------------------------------------;
  211.  
  212. Read    PROC NEAR USES ds es di         
  213.  
  214.     lds     si, [lpHeader]          ; put request header address in DS:SI
  215.  
  216.     ASSUME  si:PTR REQ_HEADER       ; to use the REQ_HEADER offsets
  217.  
  218.     mov     cx, [si].xcount         ; load cx with the size of buffer
  219.     mov     es, [si].xseg           ; load es with segment of buffer
  220.     mov     di, [si].xfer           ; load di with offset of buffer
  221.  
  222.     ASSUME  si:NOTHING              ; si will be something else now
  223.     
  224.     mov     si, cs                  ; load ds with cs again
  225.     mov     ds, si
  226.     mov     si, pAtomVal            ; load si with the string's address
  227.  
  228.     INVOKE  st_len, si, ds          ; load length of pAtomVal into ax
  229.  
  230.     .IF     (cx > ax)               ; if cx > ax, then the buffer has space
  231.         mov     bx, ax          ; to hold all the string. bx (used to
  232.         mov     cx, ax          ; return number of chars transferred)
  233.         rep     movsb           ; will hold the string length. We move
  234.                     ; into cx the length of the string, 
  235.                     ; and copy the result string into the 
  236.                     ; buffer, with the 'rep movsb' instruct
  237.     .ELSE
  238.         mov     bx, cx          ; otherwise, bx will be the size of the
  239.         dec     cx              ; the buffer. decrement cx so that we
  240.         rep     movsb           ; leave the last char of the buffer
  241.         xor     ax,ax           ; free to store a 0 to mark the end of
  242.         stosb                   ; the string.
  243.     .ENDIF
  244.  
  245.     les     di, [lpHeader]          ; re-load the request header
  246.  
  247.     ASSUME  di:PTR REQ_HEADER       ; to use the REQ_HEADER offsets
  248.  
  249.     mov     es:[di].xcount, bx      ; store the number of chars transferred
  250.  
  251.     ASSUME  di:NOTHING              ; di will be something else now
  252.  
  253.     mov     pAtomVal, offset null   ; set pAtomVal to null:won't read twice
  254.     xor     ax, ax                  ; set status to OK
  255.     ret                             ; return
  256.  
  257. Read    ENDP
  258.  
  259. ;----------------------------------------------------------------------------;
  260. ; output (write) status: if there's still memory, fine. Otherwise, error
  261. ;----------------------------------------------------------------------------;
  262.  
  263. OutStat PROC NEAR       
  264.     mov     ax, MemEnd      ; mov MemEnd into ax   
  265.     inc     ax              ; increment ax (test space for 1 more char)
  266.     .IF (ax < MaxMem)       ; if MemEnd less than MaxMem, we have space,
  267.         xor     ax, ax  ; set status to OK
  268.     .ELSE                   ; otherwise, set the Write Fault error  
  269.         mov     ax, fERROR + fWRITE_E   
  270.     .ENDIF
  271.     ret
  272. OutStat ENDP
  273.  
  274. ;----------------------------------------------------------------------------;
  275. ; function 8 = write (output) to device from program
  276. ;----------------------------------------------------------------------------;
  277. ; big chunk of the program.
  278. ;        if string: 'variable', it's a search
  279. ;        if string: 'variable=' it's a delete
  280. ;        if string: 'variable=value' it's an insert
  281. ;----------------------------------------------------------------------------;
  282.  
  283. Write PROC NEAR USES si es di cx ds     
  284.     LOCAL   func:BYTE, count:WORD, pAtom:NPTR, buf_seg:SEGPTR, buf_off:NPTR
  285.         ; func is used to see what function we're doing
  286.         ; count is used to store the length of the current atom
  287.         ; pAtom is used to point to the start of the current atom
  288.         ; buf_seg and buf_off point to the buffer to read from
  289.  
  290.     ASSUME  di:PTR REQ_HEADER       ; to use the REQ_HEADER offsets
  291.  
  292.     mov     ax, es:[di].xseg        ; load buf_seg with the buffer segment
  293.     mov     buf_seg, ax     
  294.     mov     cx, es:[di].xfer        ; load buf_off with the buffer offset
  295.     mov     buf_off, cx     
  296.  
  297.     ASSUME  di:NOTHING              ; di will be something else now
  298.  
  299.     mov     es, ax                  ; load string into ES:DI
  300.     mov     di, cx
  301.     INVOKE  st_len, di, es          ; load string length into ax, since
  302.                     ; we're not interested in xcount (for
  303.                     ; safety reasons) 
  304.     mov     count, ax               ; Store length in count
  305.     mov     cx, ax                  ; and copy to cx
  306.  
  307.  
  308. ; find if we're doing a search, delete or insert
  309.  
  310.     mov     bx, cx          ; store the count temporarily 
  311.     mov     al, '='         ; we will look in the string for a '='
  312.     repnz   scasb           ; do the scanning
  313.     .IF (ZERO?)             ; zero will be set if we found a '=',
  314.         xor     al, al  ; in which case we have to see if it's a delete
  315.         scasb           ; or insert: in a delete, the char after the 
  316.                 ; '=' has to be a 0
  317.         .IF (ZERO?)                     ; if there was a 0 after the =,
  318.             mov     func,fDELETE    ; set the function to delete
  319.         .ELSE
  320.             mov     func,fINSERT    ; else, the function is insert
  321.         .ENDIF
  322.     .ELSE
  323.         mov     func, fSEARCH   ; else (no '=' in string) it's a search
  324.     .ENDIF
  325.     inc     cx              ; cx holds amount of bytes after the '=', so 
  326.     sub     bx, cx          ; by increasing it, cx holds # of bytes after
  327.                 ; the variable name. If we substract cx from
  328.                 ; the original length (in bx), bx will have 
  329.                 ; the length of the variable only 
  330.  
  331.     .IF     (bx == 0)       ; if the length of the variable is 0 (i.e.      
  332.         xor     ax, ax  ; string= "=....", then finish. set ax to OK
  333.         jmp     wri_e   ; jump to the return place
  334.     .ENDIF
  335.  
  336.     mov     pAtomVal, offset null   ; set pAtomVal to null. only a search 
  337.                     ; will change this value, taken care
  338.                     ; of in the loops below
  339.     
  340. ; now go into the memory space and search for a matching variable. we will stop
  341. ; if we find a match or a variable that starts with a 0 (meaning no matches)
  342.  
  343.     mov     si, offset AtomList ; load SI the start of the driver memory
  344.                     ; (pointed to by AtomList, of course)
  345.         
  346. .WHILE 1                        
  347.     mov     pAtom, si       ; save the start of the current atom in memory
  348.     INVOKE  st_len, si, ds  ; length of current atom will be in ax
  349.  
  350.     .IF (ax <= 1)           ; if the length of the current atom is 1 (never
  351.         mov     di, si  ; can be 0), that means that we reached the end
  352.         mov     si, ds  ; of the driver memory. set ES:DI to DS:SI, the 
  353.         mov     es, si  ; end of memory, 
  354.         .BREAK          ; and break from the loop
  355.     .ENDIF                  
  356.  
  357.     mov     di, buf_off     ; restore di to the buffer offset
  358.     mov     cx, bx          ; load into the count reg. the variable length  
  359.         
  360.     .IF     (cx >= ax)      ; if the length of the var.name is greater than
  361.         add     si, ax  ; the whole atom length, it's sure not to be a 
  362.         .CONTINUE       ; match. increase si by the length of the 
  363.     .ENDIF                  ; current atom, and continue to next iteration
  364.     
  365.     repe    cmpsb           ; find the first non-matching character between
  366.                 ; the current atom and the string buffer, for 
  367.                 ; a maximum count of the length of the name
  368.                 
  369.     .IF (!ZERO? || (byte ptr [si]!='='))            
  370.         mov     si,pAtom; if we didn't stop because of two chars not
  371.         add     si,ax   ; matching in the repe, or if the next char
  372.     .CONTINUE               ; is not a '=' (i.e. comspec would match com),
  373.     .ENDIF                  ; increase si by the length of the atom, and
  374.                 ; go to the next iteration
  375.  
  376. ; at this point we have found a match for the requested variable
  377.                 
  378.     .IF     (func==fSEARCH) ; if we're doing a search,              
  379.         inc     si      ; increment si (to skip over the '=')
  380.         mov     pAtomVal,si     ; set pAtomVal to address of the char
  381.     
  382.     .ELSE                   ; else, we're doing a delete or insert: we have
  383.                 ; to take the current atom out of the memory.
  384.                 ; we will scan for the chars beyond our current
  385.                 ; atom. then we shift those chars to a position
  386.                 ; 'ax' places back. ax still holds the length
  387.                 ; of the current atom
  388.         mov     si,pAtom; set si to the beginning of the current atom,
  389.         mov     di, ds  ; set ES:DI to be the same as DS:SI, since
  390.         mov     es, di  ; scan uses ES:DI
  391.         mov     di, si  
  392.  
  393.         add     di, ax  ; set DI to point to the char just after the
  394.         mov     bx, ax  ; current atom. save atom length in bx.
  395.         
  396.         xor     al, al  ; clear al (will be used in the scan)
  397.         mov     cx, -1  ; cx=-1 so that the reps only stop with a match
  398.  
  399.         .WHILE 1                ; a loop:
  400.             dec     cx      ; decrease cx (because scasb doesn't)
  401.             scasb           ; and test if it's a 0 (to signal end)
  402.             .BREAK .IF ZERO?; break if the character matches
  403.             repne   scasb   ; else repeat scanning (decrements cx)
  404.         .ENDW                   ; until the next 0. then continue loop.
  405.  
  406.         not     cx      ; if cx=-(length+1), not cx=length (see st_len)
  407.  
  408.         mov     si,pAtom; get back to the beginning of the atom to
  409.         mov     di, si  ; erase. the source for the movsb will be ahead
  410.         add     si, bx  ; by bx chars (the atom length). 
  411.         rep     movsb   ; move as many chars as left in memory
  412.  
  413.         dec     di      ; for the insert following, we'll want di to            
  414.                 ; point to the end of memory. the rep took us
  415.                 ; one beyond, so we decrement it.
  416.  
  417.         sub     MemEnd,bx       ; De-allocate the memory used by atom 
  418.     
  419.     .ENDIF                  ; of (func == fSEARCH)
  420.  
  421.     .BREAK                  ; break from the loop: one match is all we need
  422.  
  423. .ENDW                           ; actually, no one gets here. they either do a
  424.                 ; continue or a break.
  425.  
  426. ; if we're doing an insert, we'll do it at the end of the memory space
  427.  
  428.     .IF (func == fINSERT)   
  429.         mov     ds, buf_seg     ; load DS:SI with the buffer to read from
  430.         mov     si, buf_off
  431.         mov     cx, count       ; cx and bx will have the count
  432.         mov     bx, cx
  433.  
  434.         add     bx, cs:MemEnd   ; add to bx the end of memory
  435.  
  436.     ; if we have a carry, it means that if bx > MaxMem, we don't have space
  437.     ; in our local AtomList. The bx>MaxMem test is not enough because if
  438.     ; MemEnd is high and adding cx goes over the segment boundary (starting
  439.     ; at 0000h), we wouldn't catch it with a bx >= MaxMem 
  440.  
  441.         .IF (CARRY? || bx >= cs:MaxMem)                         
  442.             mov     ax, fERROR + fWRITE_E   ; set write fault error
  443.             jmp     wri_e
  444.         .ELSE
  445.             mov     cs:MemEnd , bx          ; set new memory size
  446.         .ENDIF
  447.     
  448.         rep     movsb           ; copy the buffer into our memory
  449.  
  450.         xor     ax, ax          ; add a null byte to the end of strings, 
  451.         stosb                   ; to signal the end of memory
  452.     .ENDIF  
  453.  
  454.     xor ax, ax              ; set status to OK
  455.  
  456. wri_e:  ret                     ; return!
  457.  
  458. Write ENDP
  459.  
  460. ;----------------------------------------------------------------------------;
  461. ; Success: When the only thing the program needs to do is set status to OK 
  462. ;----------------------------------------------------------------------------;
  463.  
  464. Success PROC NEAR
  465.     xor     ax, ax          ; set status to OK
  466.     ret
  467. Success ENDP
  468.  
  469. ;----------------------------------------------------------------------------;
  470. ; error: set the status word to error: unknown command                       
  471. ;----------------------------------------------------------------------------;
  472. Error   PROC    NEAR            
  473.     mov     ax, fERROR + fUNKNOWN_E  ; error bit + "Unknown command" code
  474.     ret                     
  475. Error   ENDP
  476.  
  477. ;----------------------------------------------------------------------------;
  478. ; st_len
  479. ; finds the length of the string pointed to by st_off and st_seg and stores it
  480. ; in ax. no other registers are affected
  481. ;----------------------------------------------------------------------------;
  482.  
  483. st_len  PROC NEAR USES es di cx, st_off:NPBYTE, st_seg:SEGPTR   
  484.     mov     di, st_off      ; load string into ES:DI, since scas uses       
  485.     mov     es, st_seg      ; those registers
  486.     xor     al, al          ; clear al
  487.     mov     cx, -1          ; by using cx=-1, repnz won't stop until 
  488.     repnz   scasb           ; it finds a match. cx will be -(length-1) 
  489.     not     cx              ; since we use two's complement, not cx=length!
  490.     mov     ax, cx          ; mov length into ax, and
  491.     ret                     ; return
  492. st_len  ENDP    
  493.  
  494.                                 
  495. ;----------------------------------------------------------------------------;
  496. ;****************************************************************************;
  497. ;----------------------------------------------------------------------------;
  498. ;                                                                            ;
  499. ;       BEGINNING OF SPACE TO BE USED AS DRIVER MEMORY                       ;
  500. ;       ALL CODE AFTER ATOMLIST WILL BE ERASED BY THE DRIVER'S DATA          ; 
  501. ;       OR BY OTHER LOADED DRIVERS                                           ;
  502. ;                                                                            ;
  503. ;----------------------------------------------------------------------------;
  504. ;****************************************************************************;
  505. ;----------------------------------------------------------------------------;
  506.  
  507.  
  508. ;----------------------------------------------------------------------------;
  509. ;                    Initialization Data and Code               
  510. ; Only needed once, so after the driver is loaded and initialized it releases
  511. ; any memory that it won't use. The device allocates memory for its own use
  512. ; starting from 'AtomList'.
  513. ;----------------------------------------------------------------------------;
  514.  
  515. AtomList        BYTE 0
  516. Ident           BYTE '*           ATOMS Device Driver             *',lf,cr,eom
  517. Border          BYTE '*********************************************',lf,cr,eom
  518. scan_default    BYTE '*     Using default value of 1024 bytes     *',lf,cr,eom
  519. scan_ignore     BYTE '* Ignoring extra parameters in command line *',lf,cr,eom
  520. scan_minimum    BYTE '* Minimum value of 1024 bytes  will be used *',lf,cr,eom
  521. scan_overflow   BYTE '*  Maximum value of 32K bytes will be used  *',lf,cr,eom
  522. HexDump         BYTE '*   Beginning atoms memory: '
  523. Hex1            BYTE '0000h : '
  524. Hex2            BYTE '0000h   *',cr,lf
  525.         BYTE cr,'*   Ending atoms memory:            '
  526. Hex3            BYTE '0000h   *',cr,lf,eom
  527.  
  528. ;----------------------------------------------------------------------------;
  529. ; Init
  530. ;----------------------------------------------------------------------------;
  531. ; routine called only once to initialize the driver. It shows the startup 
  532. ; message, scans the config.sys command line, and tells MS-DOS how much memory
  533. ; it needs. Note that under DOS 5.0 the request header also tells you how  
  534. ; much memory you could use.
  535. ;----------------------------------------------------------------------------;
  536.  
  537. Init    PROC NEAR USES es di    
  538.  
  539.     @ShowStr Border                 ; use dos.inc macros to start the init
  540.     @ShowStr Ident                  ; message
  541.  
  542.     INVOKE  int2hex, cs, ADDR Hex1  ; get starting segment in Hex
  543.     INVOKE  int2hex,MaxMem,ADDR Hex2; get starting offset in Hex
  544.     
  545.     les     di, [lpHeader]          ; allow us to use the request values
  546.  
  547.     ASSUME  di:PTR REQ_HEADER       ; to use the REQ_HEADER OFFSETS
  548.  
  549.     ; scan the config.sys command line
  550.     INVOKE  scan, es:[di].cmd_off, es:[di].cmd_seg 
  551.     
  552.     add     ax, MemEnd              ; set ax to End of Memory relative to
  553.                     ; previous end of memory.
  554.     mov     MaxMem, ax              ; store the new value in MaxMem 
  555.     mov     es:[di].xseg,cs         ; tell MS-DOS the end of our used 
  556.     mov     es:[di].xfer,ax         ; memory (the break address)
  557.  
  558.     ASSUME  di:NOTHING              ; di will be something else now
  559.  
  560.     INVOKE  int2hex, ax, ADDR Hex3  ; Get ending offset in Hex 
  561.     @ShowStr HexDump                ; print the Hex Dump of Memory bounds
  562.     
  563.     @ShowStr Border                 ; show the end of our initial message
  564.     
  565.     xor     ax, ax                  ; return status OK
  566.     ret
  567. Init    ENDP
  568.  
  569.  
  570. ;----------------------------------------------------------------------------;
  571. ; scan
  572. ;----------------------------------------------------------------------------;
  573. ; scans the string passed by ms-dos on initialization for the amount of
  574. ; memory to allocate to the driver. Since we get a pointer to the string
  575. ; after the '=', first scan over the filename and white space, then for the
  576. ; ASCII number and turn it into decimal.
  577. ; the string we'll get might have some white space (or not) before the device
  578. ; name, then the device name, then either the end-of-line (and we'd use the
  579. ; default values) or some white space and the end-of-line (same thing) or
  580. ; some white space and a number maybe followed by some white space or the end
  581. ; of line. If there's something after the number, we'll output the extra
  582. ; parameters warning.
  583. ;
  584. ; returns:    AX    = number of bytes to allocate
  585. ;----------------------------------------------------------------------------;
  586.  
  587. scan    PROC NEAR USES ds si, st_off:NPBYTE, st_seg:SEGPTR
  588.     LOCAL   result          ; result will hold the amount to allocate
  589.     
  590.     mov     result, 0       ; clear the result variable
  591.                 
  592.     mov     ds, st_seg      ; lodsb uses DS:SI as the source string 
  593.     mov     si, st_off
  594.  
  595.     .REPEAT                 ; load from the string into the accumulator
  596.         lodsb           ; while it's white space
  597.     .UNTIL ((al!=tab) && (al!=space))
  598.  
  599.                 ; scan until we find a white space or the end
  600.                 ; of the line (note that we don't test the 1st 
  601.     .REPEAT                 ; character after the spaces: it has to be a 
  602.         lodsb           ; letter o.w. DOS wouldn't load the driver)
  603.     .UNTIL ((al==tab) || (al==space) || (al==cr) || (al==lf))
  604.  
  605.     .IF ((al==cr) || (al==lf))      ; if we found the end of line, we'll 
  606.         jmp     scan_d          ; use the default values. jump there.
  607.     .ENDIF
  608.  
  609.     .REPEAT                         ; scan for the next non-white space 
  610.         lodsb
  611.     .UNTIL ((al!=tab) && (al!=space))
  612.     
  613.     .IF ((al==cr) || (al==lf))      ; if we found the end of line, we'll 
  614.         jmp     scan_d          ; use the default values. jump there.
  615.     .ENDIF
  616.  
  617.     .REPEAT                         ; scan for the decimal numbers: 
  618.         .IF ((al<'0')||(al>'9')); if it's not an ascii decimal number
  619.         jmp     scan_i          ; ignore the parameter, use defaults
  620.         .ENDIF
  621.         cbw                     ; set ax to 0 (by extending al to ax)
  622.         sub     ax, '0'         ; turn ax into a number 0-9
  623.         mov     cx, ax          ; save the number in cx for a while,
  624.         mov     ax, result      ; because ax will be used to multiply
  625.         mov     bx, 10t         ; 'result' by 10, to add a 'digit' to
  626.         mul     bx              ; the previous 'result'
  627.         jc      scan_o          ; if we had an overflow, i.e. result
  628.                     ; is more than ffffh, then use max.val
  629.                     ; by jumping to the overflow section.   
  630.         add     ax, cx          ; else, add the units to the multiplied 
  631.         mov     result, ax      ; result, and store it where it goes
  632.         lodsb
  633.     .UNTIL ((al==tab) || (al==space) || (al==cr) || (al==lf))
  634.                     ; when we find a space, tab, cr, or lf,
  635.                     ; it's not an error: we've finished
  636.                     ; the number scan.
  637.  
  638.     .IF ((al==cr) || (al==lf))      ; if we found the end of line, we'll 
  639.         jmp     scan_d          ; use the result value so far.
  640.     .ENDIF
  641.  
  642.     .REPEAT                         ; skip over the white space, again
  643.         lodsb                   
  644.     .UNTIL ((al!=tab) && (al!=space))
  645.  
  646.     .IF ((al==cr) || (al==lf))      ; if we found the end of line, we're 
  647.         jmp     scan_d          ; fine, use the result value.
  648.     .ENDIF
  649.                     ; otherwise, we scanned something not
  650.                     ; a space or end-of-line after the 
  651.                     ; digits, so we drop into the ignore
  652.                     ; warning display.
  653.                     
  654. scan_i: 
  655.                     ; we found something extra
  656.     @ShowStr scan_ignore,cs         ; show the warning string
  657.                     ; and drop into scan_d
  658.         
  659. scan_d:                                 
  660.     push    cs                      ; make local data addressable (since
  661.     pop     ds                      ; we rewrote ds in the scanner above)
  662.     .IF (result == 0)               ; if we didn't scan anything into
  663.         @ShowStr scan_default   ; result, show a warning that we will
  664.         mov     ax, BUFMINIMUM  ; use the minimum. Set ax to minimum.
  665.     .ELSEIF (result < BUFMINIMUM)   ; if the result is less than the 
  666.         @ShowStr scan_minimum   ; minimum, display a warning and set 
  667.         mov     ax, BUFMINIMUM  ; the buffer to the minimum
  668.     .ELSEIF (result > BUFMAXIMUM)   ; if result is more than the maximum
  669.         @ShowStr scan_overflow  ; display a message to inform the user
  670.         mov     ax, BUFMAXIMUM  ; and set the buffer to the maximum
  671.     .ELSE                           ; otherwise, result is acceptable
  672.         mov     ax, result      ; set ax to result.
  673.     .ENDIF                  
  674.     ret                             ; return
  675.  
  676. scan_o:                                 ; overflows (request for >ffffh buffer)
  677.     mov     result, BUFMAXIMUM+1    ; set ax to a value that will cause  
  678.     jmp     scan_d                  ; scan_d to print out an overflow 
  679.                     ; message and set buffer to 32K
  680.  
  681. scan    ENDP
  682.  
  683.  
  684. ;----------------------------------------------------------------------------;
  685. ; int2hex 
  686. ; Converts a WORD into its hexadecimal representation. 
  687. ; Based on Chapter 4 of the MASM Programmer's guide
  688. ;----------------------------------------------------------------------------;
  689.  
  690. ; Table of hexadecimal digits
  691. hex     BYTE    '0123456789ABCDEF'
  692.  
  693. int2hex PROC NEAR USES ax bx si, number:WORD, string:NPBYTE
  694.  
  695.     mov     bx, OFFSET hex          ; load table address
  696.     mov     si, string
  697.  
  698.     mov     ax, number              ; load value to convert 
  699.     shr     ax, 12                  ; shift right to get into table index
  700.     and     ax, 0000Fh              ; remove all but least-significant byte
  701.     xlat                            ; translate
  702.     mov     [si], al                ; store as last byte
  703.  
  704.     mov     ax, number              ; load value to convert 
  705.     shr     ax, 8                   ; shift right to get into table index
  706.     and     ax, 0000Fh              ; remove all but least-significant byte
  707.     xlat                            ; translate
  708.     mov     [si+1], al              ; store as third to last byte
  709.  
  710.     mov     ax, number              ; load value to convert 
  711.     shr     ax, 4                   ; shift right to get into table index
  712.     and     ax, 0000Fh              ; remove all but least-significant byte
  713.     xlat                            ; translate
  714.     mov     [si+2], al              ; store as second to last byte
  715.  
  716.     mov     ax, number              ; load value to convert 
  717.     and     ax, 0000Fh              ; remove all but least-significant byte
  718.     xlat                            ; translate
  719.     mov     [si+3], al              ; store as last byte in string
  720.  
  721.     ret
  722.  
  723. int2hex ENDP
  724.  
  725.     END
  726.