home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
DVNOTE.ZIP
/
NOTESDEV.ASM
Wrap
Assembly Source File
|
1992-04-06
|
16KB
|
429 lines
title SAMPLE CODE CHUNKS, FROM A DEVICE DRIVER
page 60,120
;************************************************************************
; The following code sections were pruned from a 2 GIGABYTE
; VIRTUAL RAM DISK driver developed by Peter Norberg, Consulting
; (CID 76260,3355). The driver allows for 3 sets of memory to be
; managed:
; (1) a list of physical memory buffers which are not
; normally accessed by OS/2,
; (2) non-swappable memory, managed by OS/2
; (3) swappable memory, managed by OS/2
;
; They show some techniques for access of FLAT memory under OS/2
; 2.0; at this point, the comments far from complete! I recommend
; careful reading to understand the code.
;
; I hereby release this set of sample code chunks into the public
; domain, and I expressly deny any liability for it! Use this at
; your own risk -- this is intended for educational and training use.
;
; Initial assumptions:
; During your INIT phase (or delayed, if you are allocating memory
; beyond the first 16 meg), allocate your FLAT memory pointer
; using the VMA_ALLOC command. This memory may be fixed or movable,
; non-swapping or swappable. Place the returned flat address
; into the global plSwapMem pointer, with the clSwapMem counter
; describing the size of the buffer. The following code will then
; demonstrate read/write access to that buffer. You must also
; allocate 2 selectors (selGDT and selGDTUser) for use by this code.
;
; Some simplifications are possible, depending on your application.
; This code assumes that you have allocated two selectors (selGDT
; and selGDTUser) for use by the code for data copies. You may elect
; to use the calls which generate results in DS and ES as temporary
; selectors. I chose the global selectors so that interrupts could be
; enabled while accessing the data, and to allow me to keep the buffers
; addressed for as long as I like. However, since I am using global
; selectors, I had to protect the code which filled the selectors from
; reentrancy during the times the selectors are active. All of this
; is managed from the SingleThread/UnSingleThread pair and the
; higher-order DrvAccessNextBuffer and its associated DrvMapUnlock
; tool.
;
; See the "Example" code at the end for a complete access/de-select
; example
;
;************************************************************************
; Define a structure which stays on the stack, as a way of accessing
; flat memory. This struction contains the lock mechanism for that memory
;************************************************************************
SWAPACCESS STRUCT 2 ; swap
plLockedRegion dd 0 ; if fLocked is non-0, this is the start of the locked region
ulOffMem dd 0 ; Offset to memory start
clTotLeft dd 0 ; Total amount of memory left to transfer
cSingleThreaded dw 0 ; non-0 means we are single threaded
idPhysMem dw 0 ; If phys mem access, this is non-0
flocked db 0,0 ; non-0 means memory is locked by this package
achLock db 12 dup (0) ; Lock Handle
SWAPACCESS ENDS
;************************************************************************
; DATA block declarations
;************************************************************************
DGROUP group _DATA
_DATA segment dword public use16 'DATA'
... Place you device header here
...
plSwapMem dd 0 ; Gets LINEAR address in 2.0, of swap space
cbSwapMem dd 0 ; count of memory in the swap region
sem dd 0 ; Semaphore for serializing access to selectors
selGDT dw 0 ; global selector to use for DISK buffer access
selGDTUser dw 0 ; global selector to use for USER buffers
_DATA ends
_TEXT segment dword public use16 'CODE'
assume cs:_TEXT, ds:DGROUP, es:NOTHING
;************************************************************************
; SingleThread: Called with SI referencing the STACK BASED SWAPACCESS
; structure; which has already been cleared by the caller.
; Returns carry set if we were not allowed to go single threaded
;************************************************************************
SingleThread proc
inc ss:[si.SWAPACCESS.cSingleThreaded] ; Set for recursive call
cmp ss:[si.SWAPACCESS.cSingleThreaded],1 ; See if already here
je @F ; nope: do the block
clc ; else called recursively: ignore
ret ; and leave
@@:
PUSHAD
IFDEF NOSEM
@@: CLI ; No ints for a while
clc ; Set no error
test byte ptr sem,1 ; See if blocked already
jz @F ; nope: we now own it
call FinishBlock ; else block for a while
jnc @B ; if no error, retry
mov ss:[si.SWAPACCESS.cSingleThreaded],0 ; Set not blocked
mov ax,08103h ; set error
STC ; force error report
STI ; re-allow interrupts
POPAD
ret ; and return: done
@@: mov byte ptr sem,1 ; take the semaphore over
STI ; re-allow interrupts
POPAD
ret ; and return
ELSE
lea bx,sem ; get the sem address
mov ax,ds ; from our dgroup
mov cx,-1 ; set
mov di,cx ; the timeout
mov dl,DevHlp_SemRequest ; request the sem
call [fpDevHlp] ; and call the processor
jnc @F ; Worked OK
mov ss:[si.SWAPACCESS.cSingleThreaded],0 ; Set not blocked
mov ax,08103h ; set error
@@:
POPAD
ret ; and return: done
ENDIF
SingleThread endp
;************************************************************************
; UnSingleThread: Called with SI referencing the STACK BASED SWAPACCESS
; structure; which has been properlay maintained by caller and by
; SingleThread. Concept is: any unlock unlocks all!
;************************************************************************
UnSingleThread proc
test ss:[si.SWAPACCESS.cSingleThreaded],-1 ; Set for recursive call
jnz @F ; yep: we have been locked
clc ; else not locked
ret ; leave now
@@:
PUSHAD
IFDEF NOSEM
mov byte ptr sem,0 ; set sem is cleared
call FinishUnBlock ; and unblock our code
clc ; no errors
ELSE
lea bx,sem ; get the sem address
mov ax,ds ; from our dgroup
mov dl, DevHlp_SemClear ; clear it
call [fpDevHlp] ; and call the processor
ENDIF
mov ss:[si.SWAPACCESS.cSingleThreaded],0 ; Set no longer locked
POPAD
ret ; return; all done
UnSingleThread endp
;************************************************************************
; V20FlatSS: Converts SS:SI into eax FLAT address.
; dl destroyed, returns Carry SET on error, CLEAR on OK.
;************************************************************************
V20FlatSS proc ; Called with SI ref. SS memory to convert to
; flat, on ret eax = address
mov ax,ss ; Set to access
movzx esi, si ; the the memory
mov dl, DevHlp_VirtToLin ; then set to perform the Lin To Pagelist action
call [fpDevHlp] ; and do the action
ret ; And return as needed
V20FlatSS endp
;************************************************************************
; On entry, si references the SWAPACCESS structure
;************************************************************************
DrvMapUnlock proc USES eax edx bx
mov bx,ss:[si.SWAPACCESS.idPhysMem] ; get the memory access ID
or bx,bx ; See if locked
jz @F ; nope
mov ss:[si.SWAPACCESS.idPhysMem],0 ; else set no longer locked
push si ; save SI for a moment
mov si,PDNMEM_UNLOCK ; and
call CallPdnMem ; call the routine to unlock
pop si ; then
@@:
push si ; restore SI
test ss:[si.SWAPACCESS.fLocked],-1 ; See if locked
jz @F ; not locked!
mov ss:[si.SWAPACCESS.fLocked],0 ; set no longer locked
lea si,ss:[si.SWAPACCESS.achLock] ; Get the lock handle
call V20FlatSS ; the page list flat address
jc @F ; if error, ignore
mov esi,eax ; else
mov dl,DevHlp_VMUnlock ; and set
call [fpDevHlp] ; to unlock the memory
@@:
pop si ; restore ref to SWAPACCESS
call UnSingleThread ; And turn off single thread, if needed
ret ; Return: done
DrvMapUnlock ENDP
;************************************************************************
;DrvAccessNextBuffer: Map the sector passed into a temporary physical pointer
; in es:di or ds:si:
;
; On entry:
;
; DH is 0 for mapping into ds:si,
; 1 for mapping into es:di, 0 for ds:si
; 2 means do NOT assign ds or es to resulting selector
; 3 means we will write to the selector, but do not set ES or DS
;
; SI = reference to the SWAPACCESS array on entry,
; in the SS group
;
; On exit,
; DX is unchanged
; if DH was 0 or 1, the selected register pair (es:di or ds:si) are updated
; to ref the selGDT selector
; else ES, DS, SI, DI are unchanged on exit.
; ECX is the count you can access this time through.
;
; Side Effects: SWAPACCESS structure updated to reflect current conditions,
; if access legal, you will be in SINGLE THREADED mode (i.e.,
; re-entrancy to the subsystem is blocked), and selGDT will ref
; the selected memory.
;
;
; WARNING! Must be called with DS referencing our data segment!
;************************************************************************
DrvAccessNextBuffer proc near
LOCAL fbDH: WORD,
cbReq: WORD,
ulOldDI: DWORD,
npSwpa: WORD
mov fbDH,dx ; Save the storage flag
mov npSwpa,si ; save low half of SI; access to SWAPACCESS array
mov ulOldDI,edi ; save EDI
call DrvMapUnlock ; Unlock prior access
mov ecx,ss:[si.SWAPACCESS.clTotLeft] ; 0 extend the count
cmp ecx,MAX_BLOCK_XFER ; see
jbe @F ; if at end
mov ecx,MAX_BLOCK_XFER ; nope: set upper limit
@@:
mov eax, ss:[si.SWAPACCESS.ulOffMem] ; get the memory ref
add eax,plSwapMem ; Here on in the SWAP region:
cmp ecx,cbSwapMem ; see if legal
ja DrvMapBadReq ; OK
mov cbReq,cx ; save count really requested
mov ebx,eax ; for later use
mov ss:[si.SWAPACCESS.plLockedRegion],eax ; Save for later free
mov si,npSwpa ; re-get access to swap info
lea si,ss:[si.SWAPACCESS.achLock] ; Set for extract of lock handle
call V20FlatSS ; lock page list rel to SS
jc DrvMapOops ; FAILED: forget it
mov esi,eax ; save the lock handle
sub eax,eax ; Lock in place, short term
mov edi,-1 ; set no page list is to be generated
test byte ptr fbDH+1,1 ; see if write planned
jz @F ; nope
mov ax,8 ; yep: force "WE ARE WRITING" bit
@@:
mov dl,DevHlp_VMLock ; and
call [fpDevHlp] ; perform the lock
jc DrvMapOops ; FAILED: forget it
mov si,npSwpa ; restore ref to info
mov ss:[si.SWAPACCESS.fLocked],1 ; Else set LOCKED
call SingleThread ; force into single threaded mode
jc DrvMapOops ; OOPS: No single thread! forget it
mov ax,selGDT ; get the selector
; EBX is still the linear address of the buffer
; ECX is the length of the buffer
mov dl,DevHlp_LinToGDTSelector ; convert to GDT selector
call [fpDevHlp] ; do the conversion
jc DrvMapOops ; FAILED the mapping
mov si,npSwpa ; PHYS! restore ref to info
mov edi,ulOldDI ; restore EDI
mov dx,fbDH ; restore results flag
movzx ecx,cbReq ; and correct the count for excess
add ss:[si.SWAPACCESS.ulOffMem],ecx ; calculate the next access offset
sub ss:[si.SWAPACCESS.clTotLeft],ecx ; and the total left to transfer
cmp dh,2 ; are we all done?
jae DrvMapOkRet ; yep: so exit
or dh,dh ; see who gets what
jz @F ; if 0, DS:SI
sub di,di ; get the SI
mov es,selGDT ; so get it
DrvMapOkRet:
clc ; set good access
ret ; and return
@@: ; HERE on results in DS:SI
sub si,si ; get the SI
mov ds,selGDT ; DESTRY DS
ret ; and return
DrvMapBadReq:
mov eax,0AA55h ; set special error code
mov si,npSwpa ; get the handle to the lock info
call DrvMapUnlock ; go unlock it
stc ; FAILED ACTION
ret ; return quickly
DrvAccessNextBuffer endp
;************************************************************************
; Example code section which accesses some of the above
; Entered with:
; dx = Read (0) or write (1) flag
; eax = PHYSICAL address of user buffer
; ebx = offset from start of our FLAT buffer to access
; ecx = count of bytes to access
;************************************************************************
Example proc near
LOCAL fReadOrWrite: WORD,
fpbfr: DWORD,
swpa: SWAPACCESS
mov fReadOrWrite, dx ; ON ENTRY, dx = READ/WRITE flag
; 0 read, 1 write
mov fpbfr,eax ; eax = pointer to buffer to access
mov edx,ecx ; save count of bytes
lea di,swpa ; set
mov cx, sizeof swpa ; to init
sub ax,ax ; the
cld ; swap info structure
push ss ; as
pop es ; needed
rep stosb ; to all 0
... Now have code calculate where in the buffer it needs to access:
... set swpa.ulOffMem to the offset to the start of the portion of the buffer,
... set swpa.clTotLeft to the total count of bytes left to access
mov swpa.clTotLeft,edx ; save the count for access
mov swpa.ulOffMem,ebx ; save the memory offset from our flat buffer
DrvRdLp: ; PROCESS LOOP: loop here until transfer is done
cmp swpa.clTotLeft,0 ; see if any left
je DrvRWDone ; if not, leave
mov dh,2 ; Set do NOT change es/ds
add dh,byte ptr fReadOrWrite ; convert to 3, for write
lea si,swpa ; get the address of the info
call DrvAccessNextBuffer ; do the mapping, LOCK THREAD
jc DrvRwBadSec ; could not lock/map!
mov ax,word ptr fpbfr+2 ; get the user buffer
mov bx,word ptr fpbfr ; physical address
mov si,selGDTUser ; set target GDT
call PhysToGDTSel ; into our selector
jc DrvRwBadSec ; could not generate the mapping!
push ds ; Save our current segment
test byte ptr fReadOrWrite,-1 ; See if READ or WRITE
jnz @F ; WRITE: Process as needed
mov es,selGDTUser ; USER pointer and
mov ds,selGDT ; and the memory base
jmp DrvRdWrCpy ; go do the copy
@@: ; HERE for WRITE action set-up
mov es,selGDT ; and the memory base
mov ds,selGDTUser ; get the USER selector
DrvRdWrCpy:
assume DS:NOTHING
mov bx,cx ; Save the transfer count
sub di,di ; set for target offset and
sub si,si ; source offsets of 0.
cld ; Force correct string direction
rep movsb ; and transfer the buffer
; OPTIMIZATION NOTE: The above could have a SHR CX,2 \ REP MOVSD
; if it is known that the buffer size is DWORD oriented;
; or similar in-line code could detect that condition
pop ds ; Restore DS
assume DS:DGROUP ; and
; NOTE: THIS IS WHERE TC_YIELD SHOULD BE, IF USED!
mov ax,bx ; calculate
add word ptr fpBfr,bx ; to form the
adc word ptr fpBfr+2,0 ; next user buffer address to access
jmp DrvRdLp ; do next
DrvRWDone: ; HERE on all done
lea si,swpa ; get the handle to the lock info
call DrvMapUnlock ; unlock the memory
CLC ; Set OK
ret ; and report as needed
DrvRWFailed:
pop ds ; Restore DS
DrvRWBadSec:
lea si,swpa ; get the handle to the lock info
call DrvMapUnlock ; unlock the memory
mov ax,8103h ; Set DRIVE NOT READY
stc ; Set ERROR
ret ; and report in
Example endp
_TEXT ends
end