home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
ddkx86v5.zip
/
DDKX86
/
SRC
/
DEV
/
VDISK
/
VDISK.ASM
< prev
next >
Wrap
Assembly Source File
|
1995-04-14
|
80KB
|
2,168 lines
;*DDK*************************************************************************/
;
; COPYRIGHT (C) Microsoft Corporation, 1989
; COPYRIGHT Copyright (C) 1995 IBM Corporation
;
; The following IBM OS/2 WARP source code is provided to you solely for
; the purpose of assisting you in your development of OS/2 WARP device
; drivers. You may use this code in accordance with the IBM License
; Agreement provided in the IBM Device Driver Source Kit for OS/2. This
; Copyright statement may not be removed.;
;*****************************************************************************/
title Protected Mode VDisk for CP/DOS 2.0 (uses DWORD moves).
page 60,132
;/*****************************************************************************
;*
;* SOURCE FILE NAME = VDisk.asm
;*
;* DESCRIPTIVE NAME = VDisk Device Driver
;*
;*
;* VERSION V2.0
;*
;* DATE
;*
;* DESCRIPTION Implements a virtual disk in RAM. Can be loaded via
;* the following CONFIG.SYS command line:
;*
;* device = ramdrive.sys [bbbb] [ssss] [dddd]
;*
;* bbbb First numeric argument, if present, is disk size
;* in K bytes. Default value is 64. Min is 16. Max
;* is 4096 (4 Meg).
;*
;* ssss Second numeric argument, if present, is sector size
;* in bytes. Default value is 512. Allowed values are ;@mfa
;* 128, 256, 512, 1024.
;*
;* dddd Third numeric argument, if present, is the number of
;* root directory entries. Default is 64. Min is 2
;* max is 1024. The value is rounded up to the nearest
;* sector size boundary.
;* NOTE: In the event that there is not enough memory
;* to create the VDisk volume, VDisk will try to make
;* a DOS volume with 16 directory entries. This may
;* result in a volume with a different number of
;* directory entries than the dddd parameter specifies.
;*
;* FUNCTIONS VDisk_Strategy - Strategy entry point
;*
;*
;* NOTES NONE
;*
;* STRUCTURES NONE
;*
;* EXTERNAL REFERENCES
;*
;* NONE
;*
;* EXTERNAL FUNCTIONS
;*
;* NONE
;*
;* CHANGE ACTIVITY =
;* DATE FLAG APAR CHANGE DESCRIPTION
;* -------- ---------- ----- --------------------------------------
;* mm/dd/yy @Vr.mpppxx xxxxx xxxxxxx
;* 02/27/91 B718419 Special case of above ptrs
;* 07/18/91 B724590 Trap 6 in VDISK when running XGA demo
;* 08/31/92 D24793 VDISK > 4MB
;* 03/01/93 D62998 Device type & Attributes are wrong
;* 03/09/93 D63149 Sector size defaults to 512
;* 04/21/93 D64016 Allocate low, if fails then try high.
;* 08/04/93 D72412 Increase print space for disk size
;*
;*****************************************************************************/
.386p
.xlist
include mi.inc
include devhlp.inc
include basemaca.inc
include osmaca.inc
include devsym.inc
include struc.inc
include utilmid.inc
include error.inc
include strat2.inc
.list
extrn DOSGETMESSAGE:FAR
extrn DOSPUTMESSAGE:FAR
extrn DOS32FLATDS:ABS
TCYieldFlag equ 8 ; Offset into DOSVARSEG for TCResched Flag
CHUNKDWORDS equ 8192 ; Max for S/G before a Yield check
MAX_SG_ENTRIES equ 16 ; Mem Mgmt can only handle SG <= 17 entries;
attr_volume_id EQU 8 ; FAT Attribute for volume ID
VDiskData segment word public 'DATA'
VDiskData ends
VDiskCode segment word public 'CODE'
VDiskCode ends
.386p
BREAK <VDiskData Data Segment>
;/*
;** VDiskData is the Data Segment for the VDisk driver. It contains the
;** Device Header, Dummy Boot Record and BPB for the device, plus other
;** global variables to the driver.
;*/
VDiskData segment
VDisk_Attrib EQU DEV_NON_IBM + DEVLEV_3
VDiskHDR label word
dw -1, -1 ; Pointer to next device header
dw VDisk_Attrib ; Attributes of the device
dw VDisk_Strategy ; Strategy entry point
dw 0 ; Interrupt entry point
db 1 ; 1 Unit
db 7 dup (0) ; Remaining Name Chars
dw 0 ; Protect mode CS selctor of strategy
dw 0 ; Protect-mode DS selector
dw 0 ; Real-mode CS segment of strategy
dw 0 ; Real-mode DS segment
dd 1 ; Support physical address > 16M
;/*
;** This is the device driver command dispatch table.
;*/
TBLSize equ 29 ; Command Dispatch Table Size
VDiskTBL label word
dw Init ; 0 = Init
dw Media_Check ; 1 = Media Check
dw Get_BPB ; 2 = Build BPB
dw CmdErr ; 3 = Reserved
dw Read ; 4 = Read
dw CmdErr ; 5 = Non-Destruct Read NoWait
dw CmdErr ; 6 = Input Status
dw CmdErr ; 7 = Input Flush
dw Write ; 8 = Write
dw Write ; 9 = Write w/Verify
dw CmdErr ; A = Output Status
dw CmdErr ; B = Output Flush
dw CmdErr ; C = Reserved
dw RetOK ; D = Device Open (R/M)
dw RetOK ; E = Device Close (R/M)
dw Rem_Media ; F = Removable Media (R/M)
dw Ioctl ; 10 = Generic Ioctl
dw RetOK ; 11 = Reset Media
dw MapLogical ; 12 = Get Logical Drive Map
dw MapLogical ; 13 = Set Logical Drive Map
dw CmdErr ; 14 = DeInstall
dw CmdErr ; 15 = Reserved
dw CmdErr ; 16 = Get # Partitions
dw CmdErr ; 17 = Get Unit map
dw Read ; 18 = No caching read
dw Write ; 19 = No caching write
dw Write ; 1A = No caching write w/Verify
dw CmdErr ; 1B = Initialize
dw CmdErr ; 1C = Reserved
dw Get_DCS_VCS ; 1D = Get DCS/VCS
;/*
;** VDisk Bios Parameter Block and Bogus Boot Sector
;**
;** This region is a valid CP/DOS "boot sector" which contains
;** the BPB. This is used for storage of the relevant BPB parameters.
;**
;** The BOOT_START code is a very simple stub which does nothing
;** except go into an infinite loop. THIS "CODE" SHOULD NEVER
;** BE EXECUTED BY ANYONE.
;*/
assume CS:VDiskData
BOOT_SECTOR LABEL BYTE
JMP BOOT_START
DB "OS2VDISK"
VDISKBPB label word
SSIZE dw 512 ; Physical sector size in bytes ;
CSIZE db 0 ; Sectors/allocation unit
RESSEC dw 1 ; Reserved sectors for DOS
FATNUM db 1 ; No. allocation tables
DIRNUM dw 64 ; Number root directory entries
SECLIM dw 0 ; Number sectors
db 0F8H ; Media descriptor
FATSEC dw 1 ; Number of sectors per FAT (of FAT ?)
dw 1 ; Number of sectors per track
dw 1 ; Number of heads
dd 0 ; Number of hidden sectors
BIGSEC dd 0 ; Big Number of sectors ;
SEC_SHIFT db 7 ; Shifting number of ;
; sectors LEFT by this
; many bits yields #dwords
; in that many sectors.
; 128 5
; 256 6
; 512 7
; 1024 8
BOOT_START label near
JMP BOOT_START
BOOT_SIG LABEL BYTE
DB (512 - (OFFSET BOOT_SIG - OFFSET BOOT_SECTOR)) DUP ("A") ;
;/*
;** The following label is used to determine the size of the boot record
;** OFFSET BOOT_END - OFFSET BOOT_SECTOR
;*/
BOOT_END LABEL BYTE
DevHelp DD 0 ; DevHelp Function Router Address
VDisk_Base DD 0 ; Physical address of base of VDisk RAM
TCReschedFlag DD 0 ; Time Critical Thread Flag Address
TotalSectors DD 0 ; Total number of sectors ;
;/*
;** Device capabilities to be returned from command 1D.
;*/
VDisk_Caps LABEL BYTE
DW 0 ; reserved, set to zero
DB 1 ; major version of interface supported
DB 1 ; minor version of interface supported
DD GDC_DD_No_Block ; driver capabilties
DW offset VDisk_Strat2, VDiskCode ; entry point for strategy-2
DD 0 ; entry point for DD_EndOfInt
DD 0 ; entry point for DD_ChgPriority
DD 0 ; entry point for DD_SetRestPos
DD 0 ; entry point for DD_GetBoundary
;/*
;** Volume characteristics to be returned for command 1D
;*/
NUMBER_CYLINDERS EQU 1 ;
VDisk_VolChars LABEL BYTE
DW VC_RAM_DISK ; Volume descriptor
DW 0 ; Avg. seek time, milliseconds
DW 0 ; Avg latency, milliseconds
DW 0FFh ; # blocks on smallest track
DW 0FFh ; # blocks on largest track
DW 1 ; # Heads / cylinder
DD NUMBER_CYLINDERS ; # cylinders on volume ;
DD 0 ; block in center of volume (for seek)
DW 0FFh ; Max S/G list size.
NORMAL_READ EQU 0
NORMAL_WRITE EQU 1
LINEAR_READ EQU 2
LINEAR_WRITE EQU 4
.errnz (PB_READ_X - 01Eh)
.errnz (PB_WRITE_X - 01Fh)
.errnz (PB_WRITEV_X - 020h)
.errnz (PB_PREFETCH_X - 021h)
SG_VDisk_RW LABEL BYTE
DB LINEAR_READ
DB LINEAR_WRITE
DB LINEAR_WRITE
DB LINEAR_READ
DATA_END label word ; End of Data Segment after Init
BREAK <Disposable Init Data>
;/*
;** INIT data which need not be part of resident image
;*/
DEV_SIZE DD 64 ; Size in K of this device
NUM_ARG DB 1 ; Counter for order dependent numeric arguments
; bbbb,ssss,dddd seperated by commas.
NUM_NUM DB 0 ; Counter for order dependent numeric arguments
; bbbb ssss dddd seperated by spaces.
DIRSEC DW ? ; Number of directory sectors
;/*
;** Define and Statically Initialize Dummy_ReqPacket (WRITE)
;** Used for fornatting the VDisk at Init time.
;*/
Dummy_ReqPacket label word
db 0 ; Unused packet size
db 0 ; subunit number of block device
db CMDOUTPUT ; command code
dw 0 ; status word
dd 0 ; reserved
dd 0 ; device multiple-request link
db 0 ; Unused
dd 0 ; Filled at Init Time
dw 1 ; 1 sector/write
dw 0 ; start sector
;/*
;** Message texts and common data
;*/
MAXMSG EQU 1024
IvCount EQU 4
MsgFile db "OSO001.MSG", 0
MsgLen dw 0
MsgBuff db MAXMSG DUP(" ")
IvTable dd Var1
dd Var2
dd Var3
dd Var4
Var1 db "A", 0
Var2 db 6 DUP(" "), 0 ; increase from 4 DUP to 6 DUP
; so we can print "524288"
; (before, we only printed up to 4096)
Var3 db 4 DUP(" "), 0
Var4 db 4 DUP(" "), 0
;/*
;** Volume Label and Sector Buffer for Formatting the VDisk at Init
;*/
VOLID DB 'OS2VDISK ',ATTR_VOLUME_ID
DB 10 DUP (0)
DW 1100000000000000B ;12:00:00
DW 0000101011001001B ;JUN 9, 1985
DW 0,0,0
SECTOR_BUFFER DB 1024 DUP(0)
INIT_BPB DD offset VDISKBPB, VDiskData
VDiskData ends
BREAK <VDiskCode Code Segment>
VDiskCode segment
;/***************************************************************************
;*
;* FUNCTION NAME = VDisk_Strategy
;*
;* DESCRIPTION = VDisk Strategy Entry Point
;*
;* Entry point called by File System to request service.
;* Validates request block and dispatches to the correct
;* service routine in VDiskCode.
;*
;* Called in protect mode only.
;*
;* INPUT = ES: BX = ptr to request block
;* DS = VDiskData data segment
;*
;* OUTPUT = ES: BX = ptr to request block
;*
;* RETURN-NORMAL = Status set to OK in request block
;*
;* RETURN-ERROR = Status set in request block to indicate error
;*
;**************************************************************************/
Procedure VDisk_Strategy,FAR
assume CS:VDiskCode,DS:VDiskData,ES:NOTHING,SS:NOTHING
mov al, es:[bx].ReqFunc ; Command code
mov ah, TBLSize ; Valid range
.if < al be ah >
cbw ; Make command code a word
mov si, ax
add si, ax ; (si) = command offset
call VDiskTBL[si] ; go do request
.else
call CmdErr
.endif
mov es:[bx].ReqStat, ax ; Set return status
or es:[bx].ReqStat, STDON ; Set DONE bit at Init Time
ret
EndProc VDisk_Strategy
BREAK <VDisk_Strat2>
;/***************************************************************************
;*
;* FUNCTION NAME = VDisk_Strat2
;*
;* DESCRIPTION = VDisk Strategy2 Entry Point
;*
;* Entry point called by File System to request Strategy2
;* service.
;*
;* Called in protect mode only.
;*
;* This is only excepted to be called from FAT, and does not
;* support the full interface. If changes are made to the manner
;* in which Strat2 FAT calls are made, VDisk should be examined
;* for possible errors.
;*
;*
;* INPUT = ES:BX = ptr to Strat2 request list header (RLH)
;* OUTPUT = ES:BX = ptr to Strat2 request list header (RLH)
;*
;* RETURN-NORMAL =
;* RETURN-ERROR =
;*
;**************************************************************************/
Procedure VDisk_Strat2, FAR
LocalVar vs2_RLH, DWORD ; Request List Header
; Start req after last sector done
LocalVar Running_PB_Start_Block, DWORD ;
; Start after last SG entry processed
LocalVar Running_PB_SG_Array_Offset, DWORD ;
EnterProc
SaveReg <ds>
mov vs2_RLH.Segmt, es
mov vs2_RLH.Offst, bx
mov ax, VDiskData
mov ds, ax
;/*
;** Set up status/error fields of RLH
;*/
mov es:[bx].RLH_Lst_Status, RLH_Seq_In_Process
mov es:[bx].RLH_y_Done_Count, 0
mov ecx,es:[bx].RLH_Count ; ECX = # Requests
or ecx,ecx ; Requests to process?
jz Strat2_Done ; No. Return
add bx, size Req_List_Header ; ES:BX = Request Header (RH)
;/*
;** ECX = # requests to process (> 0)
;** ES:BX = Current request
;*/
request_loop:
SaveReg <ecx>
;/*
;** Setup random fields
;*/
mov es:[bx].RH_Status, RH_PROCESSING
movzx ecx, es:[bx].PB_SG_Desc_Count ; ECX = # S/G Descriptors
xor eax, eax ;
push eax ; SG entries done so far
mov Running_PB_Start_Block, 0 ; Start at zero ;
mov Running_PB_SG_Array_Offset, 0 ; Start at zero ;
SG_loop: ; More SG entries ;
RestoreReg <ecx> ; SG entries done so far
cmp cx, es:[bx].PB_SG_Desc_Count ; All SG entries done ?;
; Yes, try another List entry
jge vst30 ;
mov ax, cx ; SG entries done so far
add ax, MAX_SG_ENTRIES ; If we attempt MAX ;
.IF <ax gt es:[bx].PB_SG_Desc_Count> ; Is that > SG list ? ;
mov ax, cx ; Yes ;
; Do only as many as we need
sub ax, es:[bx].PB_SG_Desc_Count ;
neg ax ; Make positive ;
; Alrdy done + entries needed
add cx, ax ;
xchg ecx, eax ; Do this many (ecx) ;
.ELSE ; No, do MAX ;
mov cx, MAX_SG_ENTRIES ;
.ENDIF ;
; Alrdy done + about to do
SaveReg <eax> ;
mov esi, ecx
dec esi
.errnz (8 - size SG_Descriptor)
shl esi,3
movzx ebx,bx
; ECX entries into SG lst
mov eax, esi ;
add eax, size SG_Descriptor ; Next entry ;
add esi,ebx
add esi, PB_SG_Array_Offset ; ES:SI = Last SG Descriptor
; Plus SG entries prvly done
add esi, Running_PB_SG_Array_Offset ;
; Update the ones done alrdy
add Running_PB_SG_Array_Offset, eax ;
; EAX = Total Byte count
xor eax,eax ;
;/*
;** Calculate total size of request, for the PageListToLin call
;*/
vst1: add eax, es:[si].SG_BufferSize
sub esi, size SG_Descriptor
loop vst1
;/*
;** ECX = Total bytes in S/G request
;*/
mov ecx, eax
add esi, size SG_Descriptor ; ES:ESI = S/G PageList
;/*
;** Get the linear address of the S/G page list
;*/
mov ax,es ; AX:ESI = (sel,offset)
mov dl,DevHlp_VirtToLin
call DevHelp ; EAX = (linear)
;/*
;** Convert the page list to a contiguous linear region
;*/
mov edi,eax ; EDI = Lin address of S/G
; pagelist
mov dl,DevHlp_PageListToLin
call DevHelp ; EAX = Linear address of
; Region mapping pages in
; paglist.
mov edi, eax ; EDI = Linear address of
; xfer region
mov edx, ecx ; bytes count ;
shr edx, 2 ; bytes => DWORDS ;
; Shift factor DWORDS => sectors
mov cl, SEC_SHIFT ;
; Get number of sectors
shr edx, cl ;
mov ecx, edx
; mov ecx, es:[bx].PB_Block_Count ; ECX = # Sectors
mov edx, Running_PB_Start_Block ; Previous iterations
; Previous iterations+# sectors
add Running_PB_Start_Block, ecx ; to do now ;
add edx, es:[bx].PB_Start_Block ; EDX = First sector
movzx esi, es:[bx].RH_Command_Code
sub esi, PB_READ_X
movzx esi, byte ptr [esi].SG_VDisk_RW ; ESI = Read/Write
;/*
;** Call VDisk_IO to perform the actual I/O
;*/
SaveReg <bx>
call VDisk_IO_Lin
RestoreReg <bx>
or ah,ah ; Successful?
; je short vst30
jnz short error_found ;
jmp sg_loop ; See if more SG entries ;
error_found: ;
mov es:[bx].RH_Status, RH_UNREC_ERROR
jmp short Strat2_Done
vst30: mov es:[bx].RH_Status, RH_DONE
; cmp es:[bx].RH_Length, RH_LAST_REQ ; Done w/all requests?
; je short Strat2_Done ; Yes.
add bx, es:[bx].RH_Length ; ES:BX = Next Request Header
RestoreReg <ecx> ; ECX = RH Count
dec ecx
jnz request_loop ; Do the next one
Strat2_Done:
;/*
;** Call notify_address, if appropriate
;*/
les bx, vs2_RLH ; ES:BX = RLH
mov es:[bx].RLH_Lst_Status, RLH_All_Req_Done
neg ecx
add ecx, es:[bx].RLH_Count ; ECX = # Completed requests
mov es:[bx].RLH_y_Done_Count, ecx
test es:[bx].RLH_Request_Control, RLH_Notify_Done
jz short vst40
call es:[bx].RLH_Notify_Address
vst40: RestoreReg <ds>
LeaveProc
ret
EndProc VDisk_Strat2
BREAK <Media_Check>
;/***************************************************************************
;*
;* FUNCTION NAME = Media_Check
;*
;* DESCRIPTION = VDisk Media Check Routine
;* Checks state of media for block devices
;*
;* Always returns Media Unchanged
;*
;* INPUT = ES:BX = request packet address
;* DS = VDiskData
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = AX = status to return to DOS
;* Return Code set in Request Packet
;*
;* RETURN-ERROR = None
;*
;**************************************************************************/
Procedure Media_Check,NEAR,LOCAL
assume DS:VDiskData,ES:NOTHING,SS:NOTHING,DS:NOTHING
mov es:[bx.MedChkflag], 1 ; Always not changed
xor ah, ah ; NOERROR
ret
EndProc Media_Check
BREAK <Get_BPB>
;/***************************************************************************
;*
;* FUNCTION NAME = Get_BPB
;*
;* DESCRIPTION = VDisk Build BPB Routine
;* Returns pointer to BPB at VDISKBPB
;*
;* INPUT = ES:BX = request packet address
;* DS = VDiskData
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = AX = status to return to DOS
;* BPB Address set in Request Packet
;*
;* RETURN-ERROR = NONE
;*
;**************************************************************************/
Procedure Get_BPB,NEAR,LOCAL
assume DS:VDiskData,ES:NOTHING,SS:NOTHING
mov word ptr es:[bx.BldBPBpBPB], offset VDISKBPB
mov word ptr es:[bx.BldBPBpBPB+2], ds
xor ah, ah
ret
EndProc Get_BPB
BREAK <Read>
;/***************************************************************************
;*
;* FUNCTION NAME = Read
;*
;* DESCRIPTION = VDisk Read Routine
;* Performs Read Operation by calling VDisk_IO
;*
;* INPUT = ES:BX = request packet address
;* DS = VDiskData
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = AX = status to return to DOS
;*
;* RETURN-ERROR = NONE
;*
;**************************************************************************/
Procedure Read,NEAR,LOCAL
assume DS:VDiskData,ES:NOTHING,SS:NOTHING
mov si, NORMAL_READ
call VDisk_IO
ret
EndProc Read
BREAK <Write>
;/***************************************************************************
;*
;* FUNCTION NAME = Write
;*
;* DESCRIPTION = VDisk Write Routine
;* Performs Write Operation by calling VDisk_IO
;*
;* INPUT = ES:BX = request packet address
;* DS = VDiskData
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = AX = status to return to DOS
;*
;* RETURN-ERROR = NONE
;*
;**************************************************************************/
Procedure Write,NEAR,LOCAL
assume DS:VDiskData,ES:NOTHING,SS:NOTHING
mov si, NORMAL_WRITE
call VDisk_IO
ret
EndProc Write
BREAK <Rem_Media>
;/***************************************************************************
;*
;* FUNCTION NAME = Rem_Media
;*
;* DESCRIPTION = VDisk Removable Media Routine
;* Checks if Media is Removable
;*
;* Always returns media not removable
;*
;* INPUT = ES:BX = request packet address
;* DS = VDiskData
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = AX = status to return to DOS
;*
;* RETURN-ERROR = NONE
;*
;**************************************************************************/
Procedure Rem_Media,NEAR,LOCAL
assume DS:VDiskData,ES:NOTHING,SS:NOTHING
mov ax, STBUI ; Media NOT Removable (Busy = 1)
ret
EndProc Rem_Media
BREAK <MapLogical>
;/***************************************************************************
;*
;* FUNCTION NAME = MapLogical
;*
;* DESCRIPTION = VDisk Get/Set Logical Drive Routine
;* Get/Set the logical drive specified
;*
;* Since we only have one drive, always returns zero (0)
;* in Logical Unit field
;*
;* INPUT = ES:BX = request packet address
;* DS = VDiskData
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = AX = status to return to DOS
;*
;* RETURN-ERROR = NONE
;*
;**************************************************************************/
Procedure MapLogical,NEAR,LOCAL
assume DS:VDiskData,ES:NOTHING,SS:NOTHING
; mov es:[bx.Logical_Drive], 0
mov es:[bx].PktUnit, 0
xor ah, ah
ret
EndProc MapLogical
BREAK <Get_DCS_VCS>
;/***************************************************************************
;*
;* FUNCTION NAME = Get_DCS_VCS
;*
;* DESCRIPTION = VDisk Get Device/Volume characteristics
;* Returns pointers to DCS and VCS
;*
;* INPUT = ES:BX = request packet address
;* DS = VDiskData
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = AX = status to return to DOS
;* DCS Address set in Request Packet
;* VCS Address set in Request Packet
;*
;* RETURN-ERROR = NONE
;*
;**************************************************************************/
ASSUME DS:VDiskData, ES:NOTHING, FS:NOTHING
Procedure Get_DCS_VCS
mov es:[bx].Pkt_1d_DCS_Addr.Segmt,VDiskData
mov es:[bx].Pkt_1d_DCS_Addr.Offst,offset VDisk_Caps
mov es:[bx].Pkt_1d_VCS_Addr.Segmt,VDiskData
mov es:[bx].Pkt_1d_VCS_Addr.Offst,offset VDisk_VolChars
xor ah,ah
ret
EndProc Get_DCS_VCS
BREAK <RetOK>
;/***************************************************************************
;*
;* FUNCTION NAME = RetOK
;*
;* DESCRIPTION = Returns OK status
;* Returns OK status for supported do-nothing entry points
;*
;* Called in place of Open, Close, Reset Media Routines
;*
;* INPUT = ES:BX = request packet address
;* DS = VDiskData
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = AX = status to return to DOS
;*
;* RETURN-ERROR = NONE
;*
;**************************************************************************/
Procedure RetOK,NEAR,LOCAL
assume DS:VDiskData,ES:NOTHING,SS:NOTHING
xor ah, ah ; NOERROR
ret
EndProc RetOK
BREAK <CmdErr>
;/***************************************************************************
;*
;* FUNCTION NAME = CmdErr
;*
;* DESCRIPTION = Command Error
;* Returns error condition of Unknown Command
;*
;* Called when out of range request comes to VDisk_Strategy,
;* or when an unsupported function request is made.
;*
;* INPUT = ES:BX = request packet address
;* DS = VDiskData
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = AX = Status to return to DOS (ERROR + UnKnown Command Error)
;*
;* RETURN-ERROR = NONE
;*
;**************************************************************************/
Procedure CmdErr,NEAR,LOCAL
assume DS:VDiskData, ES:NOTHING, SS:NOTHING
mov ax, STERR + ERROR_I24_BAD_COMMAND
ret
EndProc CmdErr
BREAK <Ioctl>
;/***************************************************************************
;*
;* FUNCTION NAME = Ioctl
;*
;* DESCRIPTION = Generic Ioctl Support Routine
;* Supports CHKDSK use of Function 63. Get Device Parameters
;*
;* Called when an application desires the BPB of the Device.
;*
;* INPUT = ES:BX = request packet address
;* DS = VDiskData
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = AX = Status to return to DOS
;*
;* RETURN-ERROR = AX = ERROR_I24_GEN_FAILURE
;* (when packet addresses not verified)
;*
;**************************************************************************/
Procedure Ioctl,NEAR
assume DS:VDiskData, ES:NOTHING, SS:NOTHING
.if < es:[bx+GIOCategory] eq 8H > AND
.if < es:[bx+GIOFunction] eq 63H >
SaveReg <ds,es,bx,cx,si,di>
mov di, word ptr es:[bx+GIODataPack] ; (di) = offset
mov ax, word ptr es:[bx+GIODataPack+2] ; (ax) = seg/sel
mov cx, 36 ; length
mov dh, 1 ; read/write
mov dl, DevHlp_VerifyAccess
call DevHelp
.if c ; Access OK ?
mov ax, STERR+ERROR_I24_GEN_FAILURE
jmp eioctl
.endif
les di, es:[bx+GIODataPack] ; Target = User Buffer
movzx edi,di
lea esi, VDISKBPB ; Source = VDiskData:VDISKBPB
; mov cx, 21
mov ecx, 25 ;
rep movsb ; Move BPB
; mov ax, SECLIM ; 1st word of Large Sectors
; stosw
; mov ecx, 5 ; Last word of Large Sectors
; xor ax, ax ; + 3 Reserved words
; rep stosw ; + 1 Word # Cylinders (0)
mov ecx, 3 ; Last word of Large Sectors ;
xor ax, ax ; + 3 Reserved words
rep stosw ; + 1 Word # Cylinders (0)
mov ecx, 1 ;
mov ax, NUMBER_CYLINDERS ; # cylinders
rep stosw ;
mov al, 7 ; Device Type = Other
stosb
mov ax, 1+4 ; Device Attributes =
; NonRemovable+NoChangeStatus+>16MB
stosw
xor ax, ax ; NOERROR
eioctl:
RestoreReg <di,si,cx,bx,es,ds>
.else
call CmdErr
.endif
ret
EndProc Ioctl
BREAK <VDisk_IO>
;/***************************************************************************
;*
;* FUNCTION NAME = VDisk_IO
;*
;* DESCRIPTION = VDisk I/O Routine
;*
;* Performs I/O Operations to VDisk. Common worker routine for
;* Read, Write, Write w/Verify.
;*
;* This routine first takes the count parameters out of the request
;* packet. It then checks the I/O parameters for validity, then sets
;* up the parameters for the block move operation. It converts the
;* SectorCnt in CX to the # of words in that many sectors or 8000H,
;* which ever is less. It also converts the StartSector in DX into
;* a 32 bit byte offset (IO_Start) equal to that many sectors.
;*
;* NOTE that we convert the number of sectors to transfer
;* to a number of words to transfer.
;* Sector size is always a power of two, therefore a
;* multiple of two so there are no "half word" problems.
;* DOS NEVER asks for a transfer larger than 64K bytes except
;* in one case where we can ignore the extra anyway.
;* or when an unsupported function request is made.
;*
;* This routine also does all address translation for accessing
;* the VDisk, and the Time Critical considerations. Note the
;* main I/O loop is optimized for PRTECTED MODE, and we take care
;* of allowing Time Critical threads run.
;*
;* INPUT = ES:BX = request packet address
;* DS = VDiskData
;* SI = Read/Write Flag ( 0 => READ)
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = AX = status to return to DOS
;* Request packet has # of sectors transferred
;*
;* RETURN-ERROR = None
;*
;**************************************************************************/
Procedure VDisk_IO,NEAR,LOCAL
assume DS:VDiskData,ES:NOTHING,SS:NOTHING
movzx ecx, es:[bx.IOcount] ; ECX = SectorCnt ;
movzx edx, es:[bx.IOstart] ; EDX = StartSector ;
mov eax, edx ; EAX = Tmp ;
add eax, ecx ; EAX = EndSector ;
; .if < dx ae SECLIM > OR ; Validate Request
; .if < ax a SECLIM >
.if < edx ae TotalSectors > OR ; Validate Request
.if < eax a TotalSectors >
mov ax, STERR + 8 ; ERROR + SECTOR NOT FOUND
.else NEAR
mov es:[bx.IOcount], cx ; Set Request.Sectors Moved
Entry VDisk_IO_Lin,NEAR ; S/G Entry Point
localvar RWFlag, WORD
localvar DWordLimit, DWORD
localvar ChunkSize, DWORD
localvar ChunkBytes, DWORD
localvar t_es, WORD
localvar t_ds, WORD
localvar t_bx, WORD
localvar t_DTA, DWORD
localvar t_DH, DWORD
localvar t_TCReschedFlag, DWORD
localvar VDiskIO, DWORD
EnterProc
;/*
;** Convert sector count to WordLimit : Bytes/Sector = SEC_SHIFT
;*/
movzx eax, cx ; EAX = SectorCount
mov cl, SEC_SHIFT
shl eax, cl ; EAX = DWordLimit
mov DWordLimit, eax ; Save It
;/*
;** Compute the start offset of I/O from sector 0 of the VDisk
;*/
movzx eax, dx ; EAX = Starting Sector #
movzx edx, SSIZE ; EDX = Bytes/Sector
mul edx ; EDX:EAX is byte offset of start
;/*
;** Compute the 32 bit address of start of I/O on the VDisk
;*/
add eax, VDisk_Base ; EAX = Starting linear address
;/*
;** Save the DevHelp Function Router on the stack along with any
;** other variables needed from the data segment VDiskData. This is
;** because the loading of DS and ES for the transfer will destroy
;** addressibility to the VDiskData segment and the request packet.
;** Remember to restore the t_registers BEFORE removing the frame.
;*/
mov RWFlag, si
mov VDiskIO, eax ; VDiskIO
mov eax,DevHelp
mov t_DH, eax
mov eax, TCReschedFlag
mov t_TCReschedFlag, eax
test si, LINEAR_READ or LINEAR_WRITE
jnz short vdi30
mov edi, es:[bx.IOpData]
vdi30: mov ChunkSize, CHUNKDWORDS
mov t_DTA, edi ; t_DTA = DTA
mov t_es, es
mov t_ds, ds ; Addressibility
mov t_bx, bx
;/*
;** Main Transfer Loop
;*/
.while < DWordLimit a 0 > NEAR
;/*
;** Adjust ChunkSize to handle case of left overs smaller
;** than a Chunk. Also set up cx to correct size of Segment
;** to be mapped for this transfer. This should only be true
;** on the LAST iteration of the Loop(s).
;*/
mov ecx, ChunkSize
.if < DWordLimit b ecx >
mov ecx, DWordLimit
mov ChunkSize, ecx
.endif
shl ecx,2 ; DWords to Bytes
mov ChunkBytes, ecx ; Save it
;/*
;** Set up Virtual Target and Source addresses depending on function
;** Note here the physical address for IO on the VDisk is in the
;** variable VDisk_IO. The users DTA is in the local variable t_DTA as
;** a 32 bit physical address. Both addresses must be converted from
;** Physical to Virtual 32 bit addresses. The operation determines
;** which register the virtual addresses are left in. The source is
;** always left in DS:SI and the target in ES:DI. BE CAREFUL OF
;** REGISTERS BEING DESTROYED BY PHYSTOVIRT (CX?)
;*/
mov dl, DevHlp_PhysToVirt
.if < RWFlag eq NORMAL_READ >
mov ax, DOS32FLATDS
mov ds, ax
mov esi, VDiskIO
mov ecx, ChunkBytes
mov ax, t_DTAh ; Map DTA
mov bx, t_DTAl
mov dh, 1 ; DTA Target
call t_DH ; Call DevHelp
movzx edi,di
;/*
;** DS:ESI = VirtSource = VDiskIO
;** ES:EDI = VirtTarget = DTA
;*/
.elseif < RWFlag eq NORMAL_WRITE >
mov ax, DOS32FLATDS
mov es, ax
mov edi, VDiskIO
mov ecx, ChunkBytes
mov ax, t_DTAh ; Map DTA
mov bx, t_DTAl
xor dh, dh ; DTA Source
call t_DH ; Call DevHelp
movzx esi,si
;/*
;** DS:ESI = VirtSource = DTA
;** ES:EDI = VirtTarget = VDiskIO
;*/
.else ; Linear R or W
mov ax, DOS32FLATDS
mov ds, ax
mov es, ax
mov esi, VDiskIO
mov edi, t_DTA
.if <RWFlag eq LINEAR_WRITE>
xchg esi, edi
.endif
.endif
;/*
;** Addresses are mapped, so now we move a Chunk
;*/
mov ecx, ChunkSize
cld
rep movs dword ptr [esi],dword ptr [edi]
db MI_ADDRESSSIZE
nop
;/*
;** Update Counters
;*/
mov eax, ChunkSize ; EAX = ChunkSize
sub DWordLimit, eax
shl eax,2 ; EAX = ChunkSize in Bytes
add t_DTA, eax ; Move Addresses
add VDiskIO, eax
;/*
;** Test TCResched
;*/
lds bx, t_TCReschedFlag
.if < <byte ptr ds:[bx]> ne 0 > ; VOLUNTARY
mov dl, DevHlp_TCYield ; PREEMPTION
call t_DH ; Call DevHelp
.endif
.endwhile
;/*
;** Set the Status to NOERROR
;*/
xor ah, ah ; Status = NOERROR
;/*
;** Set Registers back to before the frame
;*/
mov es, t_es
mov ds, t_ds
mov bx, t_bx
LeaveProc ; Remove the local Var Frame
.endif
ret
EndProc VDisk_IO
BREAK <End OF Resident Code>
;/*
;**
;** The following label defines the end of the VDisk resident code.
;**
;*/
DEVICE_END LABEL BYTE
BREAK <Init>
;/***************************************************************************
;*
;* FUNCTION NAME = Init
;*
;* DESCRIPTION = VDisk Initialization Routine
;* Initializes the VDisk Data Segment and VDisk itself.
;*
;* VDisk Initialization routine. Its jobs are to:
;*
;* 1. Initialize various global values
;* 2. Parse the command line and set values accordingly
;* 3. Allocate the memory for the VDisk
;* 4. Initialize the DOS volume in the VDIsk RAM
;* 5. Print out report of RAMDrive parameters
;* 6. Set the return INIT I/O packet values
;*
;* At any time during the above steps an error may be detected. When
;* this happens one of the error messages is printed and VDisk
;* "de-installs" itself by returning a unit count of 0 in the INIT
;* device I/O packet. The DOS device installation code is responsible
;* for taking care of the details of re-claiming the memory used by
;* the device driver.
;*
;* Step 1 initializes the DevHelp function router address.
;*
;* Step 2 uses the "DEVICE = xxxxxxxxx" line pointer provided by
;* DOS to look for the various device parameters.
;* First we skips over the device name field
;* to get to the arguments. We then parse the arguments as they are
;* encountered. All parameter errors are detected here. NOTE THAT
;* THIS ROUTINE IS NOT RESPONSIBLE FOR SETTING DEFAULT VALUES OF
;* PARAMETER VARIABLES. This is accomplished by static initialization
;* of the parameter variables.
;*
;* Step 3 alloactes RAM for the VDisk. It must set up the following
;* global variables:
;*
;* DEV_SIZE set to TRUE size of device
;* VDisk_Base set to TRUE start of device so VDisk_IO
;* can be called
;*
;* Step 4 initializes the virtual disk. The BPB must be set, the
;* RESERVED (boot) sector, FAT sectors, and root directory sectors
;* must be initialized and written out to the VDisk. The first step
;* is to initialize all of the BPB values. The code is a typical piece
;* of PC-DOS code which given BYTES/SECTOR, TOTAL DISK SIZE
;* and NUMBER OF ROOT DIRECTORY ENTRIES inputs figures out reasonable
;* values for SEC/CLUSTER and SECTORS/FAT and TOTAL NUMBER OF CLUSTERS.
;* NOTE THAT THIS CODE IS TUNED AND SPECIFIC TO 12 BIT FATS. Don't
;* expect it to work AT ALL with a 16 bit FAT. The next step is to write
;* out the BOOT record containing the BPB to sector 0, write out
;* a FAT with all of the clusters free, and write out a root directory
;* with ONE entry (the Volume ID at VOLID).
;*
;* Step 5 makes the status report display of DEVICE SIZE, SECTOR SIZE,
;* CLUSTER SIZE, and DIRECTORY SIZE by simply printing out the values
;* from the BPB.
;*
;* Step 6 sets the INIT I/O packet return values for # of units,
;* Break address, and BPB array pointer and returns via DEVEXIT.
;*
;* INPUT = ES:BX = request packet address
;* DS = VDiskData
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = Request Packet Set
;* Units Set
;* BPB address set
;* Terminating Code Address Set
;* Terminating Data Address Set
;* AX = status to return to DOS
;*
;* RETURN-ERROR = NONE
;*
;**************************************************************************/
Procedure Init,NEAR,LOCAL
assume DS:VDiskData,ES:NOTHING,SS:NOTHING
;/*
;** Save DvHelp Function Router Address in VDiskData
;*/
mov eax, es:[bx.InitpEnd]
mov DevHelp, eax
;/*
;** Parse Command Line and Set Values Accordingly
;*/
mov al, es:[bx.Initdrv] ; DOS drive letter
add Var1, al ; Add into Global Var
;/*
;** Create a local stack frame since we are going to use
;** DS for string operations. Create local variables for those
;** values in the VDiskData segment we need to Read/Write to.
;** These rules apply from here to end of Init.
;*/
localvar t_ds, WORD
localvar t_es, WORD
localvar t_bx, WORD
EnterProc
mov t_ds, ds
mov t_es, es
mov t_bx, bx
;/*
;** Check For Out of Drives
;*/
.if <al a 25>
mov dx, msg_vdisk_no_drives
call print
jmp devabort
.endif
;/*
;** Get Address of TCResched Flag
;*/
mov al, TCYieldFlag
mov dl, DevHlp_GetDOSVar ; GetDOSVar
call DevHelp ; Call Device_Help
mov word ptr TCReschedFlag, bx
mov word ptr TCReschedFlag + 2, ax
mov bx, t_bx ; Restore BX
;/*
;** Since the LODSB instruction works with the DS register, we
;** save the values of both seg registers, and set up ES to point
;** to the VDiskData segment. The pointer to the request packet
;** is save in temp local variables.
;*/
lds si, es:[bx.InitpBPB] ; DS:SI points to config.sys
mov es, t_ds ; ES points to VDiskData
assume ds:NOTHING, es:VDiskData, ss:NOTHING
;/*
;** Skip to start of name
;*/
.repeat
lodsb
.until < al ne " " > AND
.until < al ne 09h > AND
.until < al ne "," >
;/*
;** Skip over Device Name
;*/
.while < al ne 0 > AND
.while < al ne " " > AND
.while < al ne 09h > AND
.while < al ne ",">
.if < al eq 0Dh > OR
.if < al eq 0Ah >
jmp args_donej
.endif
lodsb
.endwhile
;/*
;** Process Arguments
;*/
.while < al ne 0 > NEAR AND
.while < al ne 0Dh > NEAR AND
.while < al ne 0Ah > NEAR
.if < al eq " " > OR
.if < al eq 09h >
lodsb
.else NEAR
.if < al eq "," >
inc es:NUM_ARG
jmp next_parm
.endif
.if < al b "0" > OR
.if < al a "9" >
jmp bad_parm
.endif
;/*
;** P1795 START
;*/
inc es:NUM_NUM
push ax
mov al,es:NUM_ARG
.if <al gt es:NUM_NUM>
mov es:NUM_NUM,al
.elseif <es:NUM_NUM gt al>
inc es:NUM_ARG
.endif
pop ax
;/*
;** P1795 END
;*/
dec si
call getnum
.if < es:NUM_ARG a 3>
jmp bad_parm
.endif
.if z ; Dir Parm
.if < bx b 2> OR
.if < bx a 1024>
jmp bad_parm
.endif
;/*
;** NOTE: Since DIRNUM is the 3rd numeric arg and SSIZE is the first,
;** we know the desired sector size has been given.
;*/
mov di, es:SSIZE
mov cl, 5 ; 32 bytes per dir ent
; DI is number of dirents
shr di, cl ; in a sector
mov ax, bx
xor dx, dx
div di ; Rem in DX is partial
or dx, dx ; dir sector
.if ne ; User Specified OK number
; Figure how much user
sub di, dx ; goofed by
add bx, di ; Round UP by DI entries
.endif
mov es:DIRNUM, bx
.endif
.if < es:NUM_ARG eq 2 > ; Sector Parm
mov al, es:SEC_SHIFT
.if < bx ne 128 > AND
.if < bx ne 256 > AND
.if < bx ne 512 > AND
.if < bx ne 1024 >
jmp bad_parm
.endif
.if <bx eq 1024>
mov bx, 512
.endif
mov es:SSIZE, bx
; 512 is the default & 1024 is not allowed
.if<bx eq 128> ;
mov es:SEC_SHIFT, 5 ;
.elseif<bx eq 256> ;
mov es:SEC_SHIFT, 6 ;
.endif ;
.endif
.if < es:NUM_ARG eq 1 >
.if < ebx b 16 > OR ; 16KB ;
.if < ebx a 524288 > ; 512MB ;
jmp bad_parm
.endif
mov es:DEV_SIZE, ebx ;
.endif
next_parm: lodsb
.endif
.endwhile
args_donej:
jmp args_done
bad_parm:
mov dx, msg_vdisk_inv_parm ; Select Invalid Parameter Msg
mov ds, t_ds ; ds = VDiskData
call print
devabort: ; here the correct message is printed already
xor ax, ax ;Indicate no devices
jmp setbpb ;and return
args_done:
mov ds, t_ds ; DS = VDiskData
assume ds:VDIskData, es:NOTHING, SS:NOTHING
;/*
;** Allocate RAM for the virtual disk. If allocate FAILs print msg
;** and goto devabort. Otherwise es:VDisk_Base is set to base
;** address of VDisk RAM. Also DEV_SIZE must reflect the
;** size of the VDisk in K. (in case size was adjusted to 16K)
;** Can use most registers here. Optimize based on AllocPhys
;** Register Usage.
;*/
mov eax, DEV_SIZE ; User's Size ;
call GetRAM
.if c
mov eax, 16 ; Try 16K
mov dh, 1 ; Above 1M ; Does this work?
call GetRAM
.if c ; Insufficient Memory ?
mov dx, msg_vdisk_insuff_mem
call print
jmp devabort
.else
mov DEV_SIZE, 16
.endif
.endif
mov VDisk_Base, eax ; save VDisk_Base
;/*
;** 5. Initialize the DOS volume in the VDisk memory
;**
;**
;** We must figure out what to do.
;** All values are set so we can call VDisk_IO to read and write disk
;** SSIZE is user sector size in bytes
;** DIRNUM is user directory entries
;** DEV_SIZE is size of device in K bytes
;**
;**
;** Figure out total number of sectors in logical image
;*/
mov eax, DEV_SIZE ;
mov ecx, 1024 ;
mul ecx ; DX:AX is size in bytes of image ;
movzx ecx,SSIZE ;
div ecx ; EAX is total sectors ;
; Any remainder in DX is ignored ;
.if <eax b 10000h> ;
mov SECLIM, ax ;
mov TotalSectors, eax ;
.else ;
mov BIGSEC, eax ;
mov TotalSectors, eax ;
.endif ;
;/* ;
;** Compute # of directory sectors
;*/
mov ax, DIRNUM
shl ax, 5 ; Mult by 32 bytes per entry
; Don't need to worry about overflow, # ents
; is at most 1024
xor dx, dx
div SSIZE
.if < dx ne 0 >
inc ax
.endif
mov DIRSEC, ax ; AX is # sectors for root dir
add ax, 2 ; One reserved, At least one FAT sector???
cwde ; sign extend eax ;
.if < eax ae TotalSectors > ;
mov DIRNUM, 16 ; Smallest reasonable number
xor dx, dx
mov ax, 512 ; 16*32 = 512 bytes for dir
div SSIZE
.if < dx ne 0 >
inc ax
.endif
mov DIRSEC, ax ; AX is # sectors for root dir
add ax, 2 ; 0ne reserved, At least one FAT sector
.if < eax ae TotalSectors > ;
mov dx, msg_vdisk_insuff_mem
call print
jmp devabort
.endif
.endif
;/*
;** This is the rest of the code that sets up the BPB. It is
;** tuned for 12 bit FAT table format. DO NOT TOUCH
;*/
CLUSHOME:
;/*
;** Figure a reasonable cluster size
;*/
mov eax, TotalSectors; AX is total sectors on disk ;
movzx ebx, RESSEC
sub eax, ebx ; Sub off reserved sectors ;
mov cl, FATNUM ; CX is number of FATs
xor ch, ch
FATSUB:
movzx ebx, FATSEC ;
sub eax, ebx ; Sub off FAT sectors ;
loop FATSUB ;
movzx ebx, DIRSEC ;
; Sub off directory sectors, AX is # data sectors
sub eax, ebx ;
mov ebx, 1 ; Start at 1 sec per alloc unit ;
cmp eax, 4096-10 ;
jb cset ; 1 sector per cluster is OK ;
mov ebx, 2 ;
cmp eax, (4096-10) * 2 ;
jb cset ; 2 sector per cluster is OK ;
mov ebx, 4 ;
cmp eax, (4096-10) * 4 ;
jb cset ; 4 sector per cluster is OK ;
mov ebx, 8 ;
cmp eax, (4096-10) * 8 ;
jb cset ; 8 sector per cluster is OK ;
mov ebx, 16 ; ;
cmp eax, (4096-10) * 16 ;
jb cset ; 16 sector per cluster is OK ;
mov ebx, 32 ; ;
cmp eax, (4096-10) * 32 ;
jb cset ; 32 sector per cluster is OK ;
mov ebx, 64 ; ;
cmp eax, (4096-10) * 64 ;
jb cset ; 64 sector per cluster is OK ;
mov ebx, 128 ; ;
cmp eax, (4096-10) * 128 ;
jb cset ; 128 sector per cluster is OK ;
mov ebx, 256 ; ;
cmp eax, (4096-10) * 256 ;
jb cset ; 256 sector per cluster is OK ;
mov ebx, 512 ; ;
cmp eax, (4096-10) * 512 ;
jb cset ; 512 sector per cluster is OK ;
mov ebx, 1024 ; ;
cmp eax, (4096-10) * 1024 ;
jb cset ; 1024 sector per cluster is OK ;
mov ebx, 2048 ; 2048 sector per cluster is OK ;
CSET:
;/*
;** Figure FAT size. AX is reasonable approx to number of DATA sectors
;** BX is reasonable sec/cluster
;*/
xor edx, edx ;
div ebx ; AX is total clusters, ignore remainder ;
; can't have a "partial" cluster
mov ecx, eax ;
shr ecx, 1 ;
.if c ;
inc ecx ;
.endif ;
add eax, ecx ; AX is Bytes for fat (1.5 * # of clusters)
add eax, 3 ; Plus two reserved clusters ;
xor edx, edx ;
movzx ecx, SSIZE ;
div ecx ; AX is # sectors for a FAT this size;
.if <edx ne 0> ; if remainder ;
inc eax ; Round UP ;
.endif ; ;
; AX is # sectors for FAT
xchg ax, FATSEC ; Set newly computed value
xchg bl, CSIZE ; Set newly computed value
cmp bl, CSIZE ; Did we compute a different size?
jnz CLUSHOME ; Keep performing FATSEC and CSIZE computation
; until the values don't change.
cmp ax, FATSEC ; Did we compute a different size?
jb CLUSHOME ; Keep performing FATSEC and CSIZE computation
; until the values don't change. ;
mov FATSEC, ax ;
;/*
;** BPB is now all set !!!
;**
;** FORMAT THE VDISK IN MEMORY !!!
;**
;** Here DS = VDiskData and ES is unused.
;** In order to use VDisk_IO for formating the VDisk, we need
;** to set ES:BX to point to a dummy write request packet called
;** Dummy_ReqPacket in the VDiskData segment, and DS to VDiskData.
;** DTAs are set from either the DUMMY BOOT RECORD or SECTOR_BUFFER
;** in the VDiskData segment.
;** Dummy_ReqPacket is a write packet that is completely intialized
;** except for the DTA and StartSector.
;*/
mov ds, t_ds ; DS = VDiskData
mov es, t_ds ; ES = VDiskData
assume ds:VDiskData, es:VDiskData, ss:NOTHING
mov bx, offset Dummy_ReqPacket
; ES:BX = Dummy_ReqPacket
;/*
;** WRITE BOOT SECTOR
;*/
mov [bx.IOstart], 0 ; Sector 0
mov esi,offset BOOT_SECTOR ; DS:SI = DTA
call STUFF ; Do Request
;/*
;** WRITE FIRST FAT SECTOR
;*/
mov edi, offset SECTOR_BUFFER
xor eax, eax ; Initialize Buffer for FAT
mov ecx, 256
cld
rep stosd ; EMPTY FAT
mov edi, offset SECTOR_BUFFER
mov dword ptr [di], 0FFFFF8H ; PLUS 2 Directories
inc word ptr [bx.IOstart] ; Sector 1
mov si, offset SECTOR_BUFFER
call STUFF
inc [bx.IOstart] ; Setup Next sector
mov di, offset SECTOR_BUFFER
mov dword ptr [di], 0 ; Fix Buffer to EMPTY FAT
mov cx, FATSEC
dec cx ; First FAT sector already written
jcxz FATDONE
FATZERO:
SaveReg <cx>
mov si, NORMAL_WRITE
call iniVDisk_IO
inc [bx.IOstart] ; Next Sector (still from SECTOR_BUFFER)
RestoreReg <cx>
loop FATZERO
FATDONE:
mov si, offset VOLID ; Volume Label Dir Ent ???
call STUFF
inc [bx.IOstart] ; Next Sector
mov cx, DIRSEC
dec cx ; First Sector Dir Already Written
jcxz drive_set
DIRZERO:
SaveReg <cx>
mov si, offset SECTOR_BUFFER
call STUFF
inc [bx.IOstart] ; Next Sector(still from VOLID)
RestoreReg <cx>
loop DIRZERO
DRIVE_SET:
;/*
;** Print out Report of VDisk parameters
;*/
assume ds:VDiskData, es:NOTHING, ss:NOTHING
mov eax, DEV_SIZE ;
mov bx, offset Var2
call itoa
movzx eax, SSIZE ;
mov bx, offset Var3
call itoa
movzx eax, DIRNUM ;
mov bx, offset Var4
call itoa
mov dx, msg_vdisk_report
call print
mov al, 1 ; Number of ramdrives
;/*
;** NOTE FALL THROUGH!!!!!!!
;*/
;/*
;** SETBPB - Set INIT packet I/O return values
;**
;** This entry is used in ERROR situations to return
;** a unit count of 0 by jumping here with AL = 0.
;** The successful code path falls through to here
;** with AL = 1
;**
;** ENTRY
;** AL = INIT packet unit count
;** DS = VDiskData
;** EXIT
;** AX = status to return to DOS
;** Request Packet Set up for return from Init
;*/
SETBPB:
assume ds:VDiskData, es:NOTHING, ss:NOTHING
;/*
;** Restore ES:BX pointer to Original Init Request Packet and
;** remove local variable stack frame.
;*/
mov ds, t_ds
mov bx, t_bx
mov es, t_es
LeaveProc
;/*
;** Set Units, End of CODE and DATA Segment, Address of BPB Array
;** in Request packet
;*/
mov es:[bx.InitcUnit], al
.if <al eq 0>
mov es:[bx.InitpEnd], 0
.else
mov word ptr es:[bx.InitpEnd], offset DEVICE_END
mov word ptr es:[bx.InitpEnd+2], offset DATA_END
mov word ptr es:[bx.InitpBPB], offset INIT_BPB
mov word ptr es:[bx.InitpBPB+2], ds
.endif
ret
EndProc Init
BREAK <GetRAM>
;/***************************************************************************
;*
;* FUNCTION NAME = GetRAM
;*
;* DESCRIPTION = VDisk RAM Allocater
;* Calls DevHelp(AllocPhys) to acquire RAM for VDisk
;*
;* INPUT = AX = Size in K Desired
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = 'C' Clear
;* EAX = Linear Base Address of RAM
;*
;* RETURN-ERROR = NONE
;*
;**************************************************************************/
Procedure GetRAM,NEAR,LOCAL
mov ecx, 1024
mul ecx
mov ecx,eax ; ECX = Size of region in bytes
push ecx ;
; 0800h == Unpublished flag that will
; allow VDISK to request more
; than 4MB at init time.
mov eax,2 ; EAX = Flags (VMDHA_FIXED)
mov dl, DevHlp_VMAlloc
call DevHelp
pop ecx ;
; Try high
.IF c ;
mov eax, 2+0800h ;
mov dl, DevHlp_VMAlloc ;
call DevHelp ;
.ENDIF ;
ret
EndProc GetRAM
BREAK <iniVDisk_IO>
;/***************************************************************************
;*
;* FUNCTION NAME = iniVDisk_IO
;*
;* DESCRIPTION = VDisk VDisk_IO Caller for Init
;* Saves Registers and calls VDisk_IO
;*
;* VDisk_IO is very register destructive, all this routine
;* does is provide a less destructive way to call VDisk_IO.
;*
;* INPUT = Same as VDisk_IO
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = Same as VDisk_IO
;*
;* RETURN-ERROR = NONE
;*
;**************************************************************************/
Procedure iniVDisk_IO,NEAR,LOCAL
assume ds:VDiskData, es:NOTHING, ss:NOTHING
SaveReg <es,ds>
pushad
call VDisk_IO
popad
RestoreReg <ds,es>
ret
EndProc iniVDisk_IO
BREAK <GetNum>
;/***************************************************************************
;*
;* FUNCTION NAME = GetNum
;*
;* DESCRIPTION = Get Number
;* Read an unsigned integer
;*
;* This routine looks at DS:SI for a decimal unsigned integer.
;* It is up to the caller to make sure DS:SI points to the start
;* of a number. If it is called without DS:SI pointing to a valid
;* decimal digit the routine will return 0. Any non decimal digit
;* defines the end of the number and SI is advanced over the
;* digits which composed the number. Leading "0"s are OK.
;*
;* THIS ROUTINE DOES NOT CHECK FOR NUMBERS LARGER THAN WILL FIT
;* IN 16 BITS. If it is passed a pointer to a number larger than
;* 16 bits it will return the low 16 bits of the number.
;*
;* This routine uses the MUL instruction to multiply the running
;* number by 10 (initial value is 0) and add the numeric value
;* of the current digit. Any overflow on the MUL or ADD is ignored.
;*
;* INPUT = DS:SI -> ASCII text of number
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = BX is binary for number
;* SI advanced to point to char after number
;*
;* RETURN-ERROR = NONE
;*
;**************************************************************************/
Procedure getnum,NEAR,LOCAL
assume DS:NOTHING,ES:NOTHING,SS:NOTHING
xor ebx, ebx ;
getnum1:
lodsb
sub al, "0"
.if ae
.if < al be 9 >
cbw
cwde ;
xchg eax, ebx ;
mov edx, 10 ;
mul edx ;
add ebx, eax ;
jmp getnum1
.endif
.endif
dec si
ret
EndProc getnum
BREAK <Print>
;/***************************************************************************
;*
;* FUNCTION NAME = Print
;*
;* DESCRIPTION = Print a Message to StdOut
;* Prints a message using the MSG Retriever API
;*
;* This routine gets a message from the system message file
;* and prints it on StdOut.
;*
;* INPUT = DS = VDiskData
;* DX = ID of message to be printed:
;* msg_vdisk_inval_parm
;* msg_vdisk_insuff_mem
;* msg_vdisk_report
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = BX is binary for number
;* SI advanced to point to char after number
;*
;* RETURN-ERROR = NONE
;*
;**************************************************************************/
Procedure print,NEAR,LOCAL
assume DS:VDiskData,ES:NOTHING,SS:NOTHING
.if < dx eq msg_vdisk_report >
push ds
push offset IvTable ; Table of Variables
push IvCount ; Number of Variables
.else
push 0
push 0 ; No IvTable
push 0 ; Number of Variables
.endif
push ds
push offset MsgBuff ; Message Buffer
push MAXMSG ; Buffer Length
push dx ; Message ID
push ds
push offset MsgFile ; Msg File Name
push ds
push offset MsgLen ; Address of MsgLen
call DOSGETMESSAGE
push 1 ; StdOut
push MsgLen ; Msg Length
push ds
push offset MsgBuff ; Address of Msg Buffer
call DOSPUTMESSAGE
ret
EndProc print
BREAK <Itoa>
;/***************************************************************************
;*
;* FUNCTION NAME = Itoa
;*
;* DESCRIPTION = Integer to ASCII Routine
;* Convert an unsigned 16 bit value as a decimal integer
;* with leading zero supression. Prints from 1 to 5 digits.
;* Value 0 is "0".
;*
;* Routine uses divide instruction and a recursive call. Maximum
;* recursion is four (five digit number) plus one word on stack
;* for each level.
;*
;* INPUT = AX = Binary Value to Convert
;* DS:BX = Address for Destination
;*
;* OUTPUT =
;*
;* RETURN-NORMAL = Destination Address filled in with string
;*
;* RETURN-ERROR = NONE
;*
;**************************************************************************/
Procedure itoa,NEAR,LOCAL
assume DS:NOTHING,ES:NOTHING,SS:NOTHING
mov cx, 10
xor dx, dx
div cx ; DX is low digit, AX is higher digits
or ax, ax
.if nz
SaveReg <dx> ; Save this digit
call itoa ; Print higher digits first
RestoreReg <dx> ; Recover this digit
.endif
add dl, "0" ; Convert to ASCII
mov [bx], dl ; Move Digit to Target
inc bx ; Advance Target Pointer
ret
EndProc itoa
BREAK <Stuff>
;/***************************************************************************
;*
;* FUNCTION NAME = Stuff
;*
;* DESCRIPTION = Converts DTA to physical address, stuffs
;* it into the Request packet, and calls iniVDisk_IO
;*
;* INPUT = DS:SI = Virtual DTA
;* ES:BX = Virtual Request Packet
;* w/All fields filled except DTA
;*
;* OUTPUT = NONE
;*
;* RETURN-NORMAL =
;* RETURN-ERROR = NONE
;*
;**************************************************************************/
Procedure stuff,NEAR,LOCAL
assume DS:VDiskData, ES:VDiskData, SS:NOTHING
mov di,bx ; save bx
mov dl,DevHlp_VirtToPhys
call DevHelp ; Convert to physical address
xchg di,bx ; Get Address of request packet
mov word ptr [bx.IOpData+2], ax ; Stuff Physical DTA
mov word ptr [bx.IOpData], di
mov si, NORMAL_WRITE ; Write Flag to VDisk_IO
call iniVDisk_IO
ret
EndProc stuff
VDisk_End LABEL BYTE
VDiskCode ends
end