📄 bambi.asm
字号:
cmp es:[bx].reqfunc,19
jae no_restore_unit ; skip unit restore if so
mov al,cs:original_unit
mov byte ptr es:[bx].requnit,al
no_restore_unit:
pop ds
ret
call_dd_common endp
;-----------------------------------------------------------------------
;
; our strategy entry point
our_strat proc far
assume cs:zseg,ds:nothing,es:nothing
inc cs:in_device_call ;keep track of reentrancy
jnz reentering ;zero means first entry
finish_strategy:
mov cs:word ptr user_reqblk,bx
mov cs:word ptr user_reqblk+2,es
ret
reentering:
;
; we need to save off the packet pointer for the re-entered
; call so it will still be valid when its execution continues.
; we use our semaphore as an index into a table which will
; hold the saved packet
push di ;must preserve all regs
push es
push bx
mov di,cs:in_device_call ;treat as index into table
shl di,1 ;dword table
shl di,1
les bx,user_reqblk ;get re-entered packed
mov word ptr cs:user_save_reqblk[di],bx ;save it in our table
mov word ptr cs:user_save_reqblk[di+2],es
pop bx
pop es
pop di
jmp short finish_strategy
our_strat endp
handle_reentrancy_exit:
pop word ptr far_call_address[2]
pop word ptr far_call_address
pop word ptr dd_header[2]
pop word ptr dd_header
pop word ptr cache_unit
pop word ptr flags_and_unit
pop word ptr original_unit
;
; we need to save off the packet pointer for the re-entered
; call so it will still be valid when its execution continues.
; we use our semaphore as an index into a table which will
; hold the saved packet
push di ;must preserve all regs
push es
push bx
mov di,cs:in_device_call ;treat as index into table
shl di,1 ;dword table
shl di,1
mov bx,word ptr user_save_reqblk[di]
mov es,word ptr user_save_reqblk[di+2]
mov word ptr user_reqblk,bx
mov word ptr user_reqblk[2],es
pop bx
pop es
pop di
jmp dont_restore_stack
handle_reentrancy_enter:
push word ptr original_unit
push word ptr flags_and_unit
push word ptr cache_unit
push word ptr dd_header
push word ptr dd_header[2]
push word ptr far_call_address
push word ptr far_call_address[2]
jmp short skip_stack_swap
;-----------------------------------------------------------------------
;
; This is it! This is when DOS is trying to call a device
; driver. We intercept the call, and decide if it is something
; that can be:
;
; A) saved away into the cache
; or B) gotten from the cache
; or C) some combination of A & B
;
; Entry: our parameter block was saved in user_reqblk by
; a call to the previous routine
;
; Exit: We'll try to satisfy the request and make sure we
; fill in the status word of the request block before returning.
;
; Trashed: We should preserve ALL registers, but we trash ax, bx & es
; BUGBUG -- will ANYBODY ever care about ax, bx & es here?
; we leave es:[bx] pointing at the original req pkt.
; Flags including direction
our_int proc far
assume cs:zseg,ds:nothing,es:nothing
push ds ; save caller's ds
push cs
pop ds
assume ds:zseg ; make our data quickly addressable
inc in_bambi ; make sure we aren't reentered
; if there are no hw stacks, we may be the straw that breaks
; the stack, so we have to switch to our own stack to ensure
; no stack overflow problems
cmp in_device_call,0
jne handle_reentrancy_enter
mov save_stack_ss,ss ;save the current stack
mov save_stack_sp,sp ;context
push cs ;and switch to our own
pop ss ;internal stack
mov sp,offset resident_stack
skip_stack_swap:
push bp ; we ALWAYS need these registers saved
push dx
push cx
push es
push bx
push di
push si
; We must find the device header and unit number for the
; drive number in the request block. The unit number code
; also has a couple of flag bits in it which indicate what
; level of caching is enabled for this drive.
les bx,user_reqblk ;es:bx points to request packet
mov al,byte ptr es:[bx].requnit
mov original_unit,al ; save original unit code
call lookup_device ; remap unit code, get dev header
xor al,0c0h ; flip the 'active' bits
mov flags_and_unit,al ; save flags and unit
and al,3fh ; mask off high bits
mov cache_unit,al
mov word ptr dd_header,dx ; save actual device header
mov word ptr dd_header[2],bp
; done setting up packet structure, its off to the races!
cmp queuelength,0 ;even if caching is enabled for a drive
je just_pass_thru ;the queue size could be zero, so
;be sure there is a non-zero cache present
test flags_and_unit,0c0h ; any caching enabled?
jnz this_drive_is_cached ; if any caching, enter main module
just_pass_thru:
push si
call call_dd ; call through to device driver
pop si
return_to_caller:
jmp go_back_to_dos
; The code below will branch back up here if the operation is
; a media check for a cached drive. We must note the return
; code before returning in this case so that we can invalidate
; the cache on a media change.
do_media_check:
push si
call call_dd
pop si
cmp BYTE PTR es:[bx].14,1
je return_to_caller
;;;the following code notifies windows that the media for
;;;this drive has changed. While mutlitasking dos boxes,
;;;windows has to maintain the CDS state but can get
;;;confused when the media changes. This notification will
;;;ensure that windows keeps track of disk changes correctly
;;;code courtesy of aaronr
xor di,di
mov es,di
mov bx,15h ;dosmgr device id
mov ax,1684h ;get device api entry point
int 2fh
mov ax,es
or ax,di
jz nobodytonotify
push bp
mov bp,sp
push es
push di
mov ax,5h ;media change detected
mov bl,original_unit ;zero based drive number
call dword ptr [bp-4] ;change detected on a=0,b=1...
;
;Carry is clear if media change processed. Carry set if invalid
; drive passed in BL (drive is not in use, is out of range, is
; a network drive).
;
pop ax
pop ax
pop bp
nobodytonotify:
call reset_drive_stuff
xor bh,bh ;cause the bpb to be re-read next access
mov bl,original_unit
shl bx,1
mov word ptr secsize_and_align_info[bx],0ffffh
mov selected_drive,-1
jmp go_back_to_dos
handle_get_bpb:
call reset_drive_stuff
jmp short not_get_bpb
; We now know we're operating on a cached drive. We have to
; look at the function number to decide what to do next.
this_drive_is_cached:
cmp es:[bx].reqfunc,devrd ; read, write, writev == go do it
je cache_this_readwrite
cmp es:[bx].reqfunc,devwrt
je cache_this_readwrite
cmp es:[bx].reqfunc,devwrtv
je cache_this_readwrite
cmp es:[bx].reqfunc,1
je do_media_check ; watch return code from media check
cmp es:[bx].reqfunc,2
je handle_get_bpb
not_get_bpb:
cmp es:[bx].reqfunc,genioctl ; certain genioctls require
jnz just_pass_thru ; that we invalidate our cache
mov ax,word ptr es:[bx].majorfunction
cmp ax,6008h ;dont trap get bpb
je just_pass_thru_short
cmp ax,6608h
jae just_pass_thru_short
call reset_drive_stuff
just_pass_thru_short:
jmp just_pass_thru
; We've now qualified this operation substantially. We know that
; the request was a read, write, or write w/verify on a drive that
; we are caching. Furthermore, the cache is not zero-length and
; it isn't a Win 3.1 swap file access.
cache_this_readwrite:
mov al,es:[bx].0dh ; use correct media id
mov media_id,al
mov al,es:[bx] ; use correct packet size
mov packet_size,al
cld ; allow our internals to assume direction
; have we changed drives from last time?
mov al,original_unit ; no need to recalculate constants
cmp al,selected_drive ; on repeated accesses to the same
jz constants_already_valid ; drive
; setup the relevant constants for this drive
mov selected_drive,al
call get_drive_info
mov byte ptr cache_align_factor,ch ; save cache align factor
mov byte ptr cache_block_shift,cl ; save shift factor
push bx
xor bh,bh
mov bl,original_unit
mov al,media_id
mov byte ptr media_ids[bx],al
mov al,packet_size
mov byte ptr packet_sizes[bx],al
pop bx
mov ax,cache_block_bytes ; get sector size
shr ax,cl
mov sector_size_bytes,ax
mov al,1
shl al,cl
mov byte ptr cache_block_sectors,al ; get number of secs / cache block
; Now we have to generate a mask with one bit per sectors/blk
mov cl,al
mov ax,1
shl ax,cl
dec ax ; get one bit per sector in this block
mov cache_mask,ax ; and save the mask
constants_already_valid:
;
; When we come here, we know that variables like sector_size_bytes
; and cache_block_sectors are set correctly to their values for the
; drive for this operation. The caller's registers have mostly been
; saved on the stack. It is time to go to work.
mov ax,es:[bx.count] ; copy count & transfer address to
mov d_count,ax ; local temporary variables
mov ax,es:word ptr [bx.trans]
mov word ptr d_trans,ax
mov ax,es:word ptr [bx.trans+2]
mov word ptr d_trans+2,ax
mov ax,es:[bx.start] ; get 16-bit block number
xor dx,dx
; We must look at our device header (original copy) to see
; if we should support 32 bit sector numbers.
push es
push bx
les bx,dd_header
test es:byte ptr [bx.4],2 ; 32-bit capable device?
pop bx
pop es
jz use_16_bit_blocknum
cmp dos_3x,0
je notdos3x
cmp byte ptr es:[bx],24 ;compaq's 24 byte packet?
jne use_16_bit_blocknum
mov dx,es:[bx.start.2] ;get second part of 32-bit offset
jmp short use_16_bit_blocknum
notdos3x:
cmp ax,-1 ; for 32 bit block numbers, the
jnz use_16_bit_blocknum ; low 16 should be -1
mov dx,es:[bx.start_h] ; get high part
mov ax,es:[bx.start_l] ; get 32-bit block number
use_16_bit_blocknum:
; We now have dx:ax == the starting sector for this I/O. Let's
; figure out which cache block contains that sector, and what the
; sector offset within that cache block is
; cmp es:byte ptr [bx.1],1 ; drive b:
; jnz no_bkpt
; cmp ax,0b10h
; jb no_bkpt
;
; int 3
;
;no_bkpt:
add ax,cache_align_factor ; adjustment factor attempts to align
adc dx,0 ; cache blocks with FAT clusters
mov cx,cache_block_shift
push ax ; save the low bits
; The following is a fast 32-bit shift right. It will be used
; elsewhere in this program without the timing documentation.
; WARNING: assumes CL <=16!
mov di,0FFFFh ;2 clks 3bytes set up to make a mask
ror dx,cl ;3, 2bytes low bits of dx set to final bit position
shr ax,cl ;3, 2bytes low bits of ax set to final bit position
shr di,cl ;3, 2bytes the mask should just cover low bits in dx
mov cx,dx ;2, 2bytes save off dx (we still need high bits)
and dx,di ;2, 2bytes mask off the high bits of dx--dx is is done
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -