IBM vdisk.asm (1/3)
From
T. Ment@21:1/5 to
All on Tue Jun 23 03:10:55 2020
IBM provided source code for vdisk 3.3 but omitted some dependencies as mentioned in the .asm file:
Message text for VDISK is in module VDISKMSG.
include versiona.inc
In DOS version 4.0 they bundled everything into vdisk.asm, and now it
builds with MASM 4.0.
Hard to find, but PCjs Machines had it. And if that fails, here it is.
PAGE ,132
TITLE VDISK - Virtual Disk Device Driver
;VDISK simulates a disk drive, using Random Access Memory as the storage medium.
;This program is meant to serve as an example of a device driver. It does not ;reflect the current level of VDISK.SYS.
;(C) Copyright IBM Corporation, 1984,1985,1988
;Licensed Material - Program Property of IBM
;Authors: Dick Dievendorff
; Rod Springhetti
; David M. Sewell
;Add the following statement to CONFIG.SYS
; DEVICE=[d:][path]VDISK.SYS bbb sss ddd [/E:m]
; where: bbb is the desired buffer size (in kilobytes)
; minimum 1KB, maximum is size of available memory,
; default is 64KB.
; VDISK will leave at least 64KB of available memory,
; although subsequent device drivers (other than VDISK)
; other programs that make themselves resident, and
; COMMAND.COM will result in less than 64KB as shown
; by CHKDSK.
; Must be large enough for 1 boot sector + FAT sectors
; + 1 directory sector + at least 1 data cluster,
; or the device driver won't be installed.
; sss is the desired sector size (in bytes)
; 128, 256, or 512, default is 128.
; Will be adjusted if number of FAT entries > 0FE0H
; ddd is the desired number of directory entries
; Minimum 2, maximum 512, default 64.
; Will be rounded upward to sector size boundary.
; /E may only be used if extended memory above 1 megabyte
; is to be used. INT 15H functions 87H and 88H are used
; to read and write this extended memory.
; The m parameter in the /E option specifies the maximum
; number of sectors that the VDISK will transfer at a time.
; Optional values are 1,2,3,4,5,6,7 or 8 sectors, the default
; is 8 sectors.
; Brackets indicate optional operands.
; Samples:
; DEVICE=\path\VDISK.SYS 160 512 64
; results in a 160KB VDISK, with 512 byte sectors, 64 directory entries
; DEVICE=VDISK.SYS Buffersize 60 Sectorsize 128 Directory entries 32
; (since only numbers are interpreted, you may comment the line with
; non-numeric characters)
SUBTTL Structure Definitions
PAGE ;-----------------------------------------------------------------------;
; Request Header (Common portion) ; ;-----------------------------------------------------------------------;
RH EQU DS:[BX] ;addressability to Request Header structure
RHC STRUC ;fields common to all request types
DB ? ;length of Request Header (including data)
DB ? ;unit code (subunit)
RHC_CMD DB ? ;command code
RHC_STA DW ? ;status
DQ ? ;reserved for DOS
RHC ENDS ;end of common portion
CMD_INPUT EQU 4 ;RHC_CMD is INPUT request
;status values for RHC_STA
STAT_DONE EQU 01H ;function complete status (high order byte) STAT_CMDERR EQU 8003H ;invalid command code error
STAT_CRC EQU 8004H ;CRC error
STAT_SNF EQU 8008H ;sector not found error
STAT_BUSY EQU 0200H ;busy bit (9) for Removable Media call ;-----------------------------------------------------------------------;
; Request Header for INIT command ; ;-----------------------------------------------------------------------;
RH0 STRUC
DB (TYPE RHC) DUP (?) ;common portion
RH0_NUN DB ? ;number of units
;set to 1 if installation succeeds,
;set to 0 to cause installation failure RH0_ENDO DW ? ;offset of ending address
RH0_ENDS DW ? ;segment of ending address
RH0_BPBO DW ? ;offset of BPB array address
RH0_BPBS DW ? ;segment of BPB array address
RH0_DRIV DB ? ;drive code (DOS 3 only)
RH0 ENDS
RH0_BPBA EQU DWORD PTR RH0_BPBO ;offset/segment of BPB array address ;Note: RH0_BPBA at entry to INIT points to all after DEVICE= on CONFIG.SYS stmt
;-----------------------------------------------------------------------;
; Request Header for MEDIA CHECK Command ; ;-----------------------------------------------------------------------;
RH1 STRUC
DB (TYPE RHC) DUP (?) ;common portion
DB ? ;media descriptor
RH1_RET DB ? ;return information
RH1 ENDS ;-----------------------------------------------------------------------;
; Request Header for BUILD BPB Command ; ;-----------------------------------------------------------------------;
RH2 STRUC
DB (TYPE RHC) DUP(?) ;common portion
DB ? ;media descriptor
DW ? ;offset of transfer address
DW ? ;segment of transfer address
RH2_BPBO DW ? ;offset of BPB table address
RH2_BPBS DW ? ;segment of BPB table address
RH2 ENDS ;-----------------------------------------------------------------------;
; Request Header for INPUT, OUTPUT, and OUTPUT with verify ; ;-----------------------------------------------------------------------;
RH4 STRUC
DB (TYPE RHC) DUP (?) ;common portion
DB ? ;media descriptor
RH4_DTAO DW ? ;offset of transfer address
RH4_DTAS DW ? ;segment of transfer address
RH4_CNT DW ? ;sector count
RH4_SSN DW ? ;starting sector number
RH4 ENDS
RH4_DTAA EQU DWORD PTR RH4_DTAO ;offset/segment of transfer address
;-----------------------------------------------------------------------;
; Segment Descriptor (part of Global Descriptor Table) ; ;-----------------------------------------------------------------------;
DESC STRUC ;data segment descriptor
DESC_LMT DW 0 ;segment limit (length)
DESC_BASEL DW 0 ;bits 15-0 of physical address
DESC_BASEH DB 0 ;bits 23-16 of physical address
DB 0 ;access rights byte
DW 0 ;reserved
DESC ENDS
SUBTTL Equates and Macro Definitions
PAGE
MEM_SIZE EQU 12H ;BIOS memory size determination INT
;returns system size in KB in AX
EM_INT EQU 15H ;extended memory BIOS interrupt INT
EM_BLKMOVE EQU 87H ;block move function
EM_MEMSIZE EQU 88H ;memory size determination in KB
BOOT_INT EQU 19H ;bootstrap DOS
DOS EQU 21H ;DOS request INT
DOS_PCHR EQU 02H ;print character function
DOS_PSTR EQU 09H ;print string function
DOS_VERS EQU 30H ;get DOS version
TAB EQU 09H ;ASCII tab
LF EQU 0AH ;ASCII line feed
CR EQU 0DH ;ASCII carriage return
BEL EQU 07H ;ASCII bell
PARA_SIZE EQU 16 ;number of bytes in one 8088 paragraph DIR_ENTRY_SIZE EQU 32 ;number of bytes per directory entry
MAX_FATE EQU 0FE0H ;largest number of FAT entries allowed
;default values used if parameters are omitted
DFLT_BSIZE EQU 64 ;default VDISK buffer size (KB)
DFLT_SSZ EQU 128 ;default sector size
DFLT_DIRN EQU 64 ;default number of directory entries
DFLT_ESS EQU 8 ;default maximum sectors to transfer
MIN_DIRN EQU 2 ;minimum number of directory entries
MAX_DIRN EQU 512 ;maximum number of directory entries
STACK_SIZE EQU 512 ;length of stack during initialization
;-----------------------------------------------------------------------;
; MSG invokes the console message subroutine ; ;-----------------------------------------------------------------------;
MSG MACRO TEXT
PUSH DX ;;save DX across call
MOV DX,OFFSET TEXT ;;point to message
CALL SHOW_MSG ;;issue message
POP DX
ENDM
SUBTTL Resident Data Area
PAGE ;-----------------------------------------------------------------------;
; Map INT 19H vector in low storage ; ;-----------------------------------------------------------------------; INT_VEC SEGMENT AT 00H
ORG 4*BOOT_INT
BOOT_VEC LABEL DWORD
BOOT_VECO DW ? ;offset
BOOT_VECS DW ? ;segment
INT_VEC ENDS
CSEG SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:CSEG ;-----------------------------------------------------------------------;
; Resident data area. ;
; ;
; All variables and constants required after initialization ;
; part one are defined here. ; ;-----------------------------------------------------------------------;
START EQU $ ;begin resident VDISK data & code
;DEVICE HEADER - must be at offset zero within device driver
DD -1 ;becomes pointer to next device header
DW 0800H ;attribute (IBM format block device)
;supports OPEN/CLOSE/RM calls
DW OFFSET STRATEGY ;pointer to device "strategy" routine
DW OFFSET IRPT ;pointer to device "interrupt handler"
DB 1 ;number of block devices
DB 7 DUP (?) ;7 byte filler (remainder of 8-byte name)
;END OF DEVICE HEADER
;This volume label is placed into the directory of the new VDISK
;This constant is also used to determine if a previous extended memory VDISK ;has been installed.
VOL_LABEL DB 'VDISK ' ;00-10 volume name (shows program level)
DB 28H ;11-11 attribute (volume label)
DT 0 ;12-21 reserved
DW 6000H ;22-23 time=12:00 noon
DW 0986H ;24-25 date=12/06/84
VOL_LABEL_LEN EQU $-VOL_LABEL ;length of volume label
;The following field, in the first extended memory VDISK device driver,
;is the 24-bit address of the first free byte of extended memory.
;This address is not in the common offset/segment format.
;The initial value, 10 0000H, is 1 megabyte.
AVAIL_LO DW 0 ;address of first free byte of
AVAIL_HI DB 10H ;extended memory
;The INT 19H vector is "stolen" by the first VDISK installed in extended memory.
;The original content of the interrupt vector is saved here.
INTV19 LABEL DWORD
INTV19O DW ? ;offset
INTV19S DW ? ;segment
PARAS_PER_SECTOR DW ? ;number of 16-byte paragraphs in one sector
START_BUFFER_PARA DW ? ;segment address of start of VDISK buffer
;for extended memory, this segment address
;is the end of the VDISK device driver.
EM_SW DB 0 ;non-zero if Extended Memory
EM_STAT DW 0 ;AX from last unsuccessful extended memory I/O
START_EM_LO DW ? ;24-bit address of start of VDISK buffer START_EM_HI DB ? ;(extended memory only)
WPARA_SIZE DW PARA_SIZE ;number of bytes in one paragraph
MAX_CNT DW ? ;(0FFFFH/BPB_SSZ) truncated, the maximum
;number of sectors that can be transferred
;without worrying about 64KB wrap
SECT_LEFT DW ? ;sectors left to transfer
IO_SRCA LABEL DWORD ;offset/segment of source
IO_SRCO DW ? ;offset
IO_SRCS DW ? ;segment
IO_TGTA LABEL DWORD ;offset/segment of target
IO_TGTO DW ? ;offset
IO_TGTS DW ? ;segment
;-----------------------------------------------------------------------;
; BIOS Parameter Block (BPB) ; ;-----------------------------------------------------------------------;
;This is where the characteristics of the virtual disk are established.
;A copy of this block is moved into the boot record of the virtual disk.
;DEBUG can be used to read sector zero of the virtual disk to examine the
;boot record copy of this block.
BPB LABEL BYTE ;BIOS Parameter Block (BPB)
BPB_SSZ DW 0 ;number of bytes per disk sector
BPB_AUSZ DB 1 ;sectors per allocation unit
BPB_RES DW 1 ;number of reserved sectors (for boot record) BPB_FATN DB 1 ;number of File Allocation Table (FAT) copies BPB_DIRN DW 0 ;number of root directory entries
BPB_SECN DW 1 ;total number of sectors
;computed from buffer size and sector size
;(this includes reserved, FAT, directory,
;and data sectors)
BPB_MCB DB 0FEH ;media descriptor byte
BPB_FATSZ DW 1 ;number of sectors occupied by a single FAT
;computed from BPBSSZ and BPBSECN
BPB_LEN EQU $-BPB ;length of BIOS parameter block
BPB_PTR DW BPB ;BIOS Parameter Block pointer array (1 entry) ;-----------------------------------------------------------------------;
; Request Header (RH) address, saved here by "strategy" routine ; ;-----------------------------------------------------------------------; RH_PTRA LABEL DWORD
RH_PTRO DW ? ;offset
RH_PTRS DW ? ;segment ;-----------------------------------------------------------------------;
; Global Descriptor Table (GDT), used for extended memory moves ; ;-----------------------------------------------------------------------; ;Access Rights Byte (93H) is
; P=1 (segment is mapped into physical memory)
; E=0 (data segment descriptor)
; D=0 (grow up segment, offsets must be <= limit)
; W=1 (data segment may be written into)
; DPL=0 (privilege level 0)
GDT LABEL BYTE ;begin global descriptor table
DESC <> ;dummy descriptor
DESC <> ;descriptor for GDT itself
SRC DESC <,,,93H,> ;source descriptor
TGT DESC <,,,93H,> ;target descriptor
DESC <> ;BIOS CS descriptor
DESC <> ;stack segment descriptor
SUBTTL INT 19H (boot) interrupt handler
PAGE ;-----------------------------------------------------------------------;
; INT 19H Interrupt Handler routine ; ;-----------------------------------------------------------------------;
;The INT 19H vector is altered by VDISK initialization to point to this ;routine within the first extended memory VDISK device driver.
;The vector points to the device driver so that subsequent VDISKs installed
;in extended memory can find the first one to determine what memory has ;already been allocated to VDISKs.
;This routine restores the original INT 19H vector's content, then jumps
;to the original routine.
;INT 19H, the "Boot" INT, is always altered when DOS is booted.
;This routine is entered with interrupts disabled.
VDISK_INT19 PROC ;INT 19H received
PUSH DS ;save registers we're going to alter
PUSH AX
XOR AX,AX
MOV DS,AX ;set DS = 0
ASSUME DS:INT_VEC
MOV AX,CS:INTV19O ;get offset of saved vector
MOV DS:BOOT_VECO,AX ;store offset in interrupt vector
MOV AX,CS:INTV19S ;get segment of saved vector
MOV DS:BOOT_VECS,AX ;store segment in interrupt vector
POP AX
POP DS
JMP CS:INTV19 ;go to original interrupt routine
VDISK_INT19 ENDP
ASSUME DS:NOTHING
SUBTTL Device Strategy & interrupt entry points
PAGE ;-----------------------------------------------------------------------;
; Device "strategy" entry point ;
; ;
; Retain the Request Header address for use by Interrupt routine ; ;-----------------------------------------------------------------------; STRATEGY PROC FAR
MOV CS:RH_PTRO,BX ;offset
MOV CS:RH_PTRS,ES ;segment
RET
STRATEGY ENDP ;-----------------------------------------------------------------------;
; Table of command processing routine entry points ; ;-----------------------------------------------------------------------; CMD_TABLE LABEL WORD
DW OFFSET INIT_P1 ; 0 - Initialization
DW OFFSET MEDIA_CHECK ; 1 - Media check
DW OFFSET BLD_BPB ; 2 - Build BPB
DW OFFSET INPUT_IOCTL ; 3 - IOCTL input
DW OFFSET INPUT ; 4 - Input
DW OFFSET INPUT_NOWAIT ; 5 - Non destructive input no wait
DW OFFSET INPUT_STATUS ; 6 - Input status
DW OFFSET INPUT_FLUSH ; 7 - Input flush
DW OFFSET OUTPUT ; 8 - Output
DW OFFSET OUTPUT_VERIFY ; 9 - Output with verify
DW OFFSET OUTPUT_STATUS ;10 - Output status
DW OFFSET OUTPUT_FLUSH ;11 - Output flush
DW OFFSET OUTPUT_IOCTL ;12 - IOCTL output
DW OFFSET DEVICE_OPEN ;13 - Device OPEN
DW OFFSET DEVICE_CLOSE ;14 - Device CLOSE
MAX_CMD EQU ($-CMD_TABLE)/2 ;highest valid command follows
DW OFFSET REMOVABLE_MEDIA ;15 - Removable media
;-----------------------------------------------------------------------;
; Device "interrupt" entry point ; ;-----------------------------------------------------------------------;
IRPT PROC FAR ;device interrupt entry point
PUSH DS ;save all registers modified
PUSH ES
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH DI
PUSH SI
;BP isn't used, so it isn't saved
CLD ;all moves forward
LDS BX,CS:RH_PTRA ;get RH address passed to "strategy" into DS:BX
MOV AL,RH.RHC_CMD ;command code from Request Header
CBW ;zero AH (if AL > 7FH, next compare will
;catch that error)
CMP AL,MAX_CMD ;if command code is too high
JA IRPT_CMD_HIGH ;jump to error routine
MOV DI,OFFSET IRPT_CMD_EXIT ;return addr from command processor
PUSH DI ;push return address onto stack
;command routine issues "RET"
ADD AX,AX ;double command code for table offset
MOV DI,AX ;put into index register for JMP
XOR AX,AX ;initialize return to "no error"
;At entry to command processing routine:
; DS:BX = Request Header address
; CS = VDISK code segment address
; AX = 0
; top of stack is return address, IRPT_CMD_EXIT
JMP CS:CMD_TABLE[DI] ;call routine to handle the command
IRPT_CMD_ERROR: ;CALLed for unsupported character mode commands
INPUT_IOCTL: ;IOCTL input
INPUT_NOWAIT: ;Non-destructive input no wait
INPUT_STATUS: ;Input status
INPUT_FLUSH: ;Input flush
OUTPUT_IOCTL: ;IOCTL output
OUTPUT_STATUS: ;Output status
OUTPUT_FLUSH: ;Output flush
POP AX ;pop return address off stack
IRPT_CMD_HIGH: ;JMPed to if RHC_CMD > MAX_CMD
MOV AX,STAT_CMDERR ;"invalid command" and error
IRPT_CMD_EXIT: ;return from command routine
;AX = value to OR into status word
LDS BX,CS:RH_PTRA ;restore DS:BX as Request Header pointer
OR AH,STAT_DONE ;add "done" bit to status word
MOV RH.RHC_STA,AX ;store status into request header
POP SI ;restore registers
POP DI
POP DX
POP CX
POP BX
POP AX
POP ES
POP DS
RET
IRPT ENDP
SUBTTL Command Processing routines
PAGE ;-----------------------------------------------------------------------;
; Command Code 1 - Media Check ;
; At entry, DS:BX point to request header, AX = 0 ; ;-----------------------------------------------------------------------; MEDIA_CHECK PROC
MOV RH.RH1_RET,1 ;indicate media not changed
RET ;AX = zero, no error
MEDIA_CHECK ENDP ;-----------------------------------------------------------------------;
; Command Code 2 - Build BPB ;
; At entry, DS:BX point to request header, AX = 0 ; ;-----------------------------------------------------------------------; BLD_BPB PROC
MOV RH.RH2_BPBO,OFFSET BPB ;return pointer to our BPB
MOV RH.RH2_BPBS,CS
RET ;AX = zero, no error
BLD_BPB ENDP ;-----------------------------------------------------------------------;
; Command Code 13 - Device Open ;
; Command Code 14 - Device Close ;
; Command Code 15 - Removable media ;
; At entry, DS:BX point to request header, AX = 0 ; ;-----------------------------------------------------------------------; REMOVABLE_MEDIA PROC
MOV AX,STAT_BUSY ;set status bit 9 (busy)
;indicating non-removable media DEVICE_OPEN: ;NOP for device open
DEVICE_CLOSE: ;NOP for device close
RET
REMOVABLE_MEDIA ENDP ;fall thru to return ;-----------------------------------------------------------------------;
; Command Code 4 - Input ;
; Command Code 8 - Output ;
; Command Code 9 - Output with verify ;
; At entry, DS:BX point to request header, AX = 0 ; ;-----------------------------------------------------------------------;
INOUT PROC
INPUT:
OUTPUT:
OUTPUT_VERIFY:
;Make sure I/O is entirely within the VDISK sector boundaries
MOV CX,CS:BPB_SECN ;get total sector count
MOV AX,RH.RH4_SSN ;starting sector number
CMP AX,CX ;can't exceed total count
JA INOUT_E1 ;jump if start > total
ADD AX,RH.RH4_CNT ;start + sector count
CMP AX,CX ;can't exceed total count
JNA INOUT_A ;jump if start + count <= total
INOUT_E1: ;I/O not within VDISK sector boundaries
MOV RH.RH4_CNT,0 ;set sectors transferred to zero
MOV AX,STAT_SNF ;indicate 'Sector not found' error
RET ;return with error status in AX
INOUT_A: ;I/O within VDISK bounds
MOV AX,RH.RH4_CNT ;get sector count
MOV CS:SECT_LEFT,AX ;save as sectors left to process
CMP CS:EM_SW,0 ;extended memory mode?
JNE INOUT_EM ;jump to extended memory I/O code
;Compute offset and segment of VDISK buffer for starting segment in CX:SI
MOV AX,RH.RH4_SSN ;starting sector number
MUL CS:PARAS_PER_SECTOR ;* length of one sector in paragraphs
ADD AX,CS:START_BUFFER_PARA ;+ segment of VDISK buffer sector 0
MOV CX,AX ;segment address to CX
XOR SI,SI ;offset is zero
;Compute address of caller's Data Transfer Addr in DX:AX with smallest offset, ;so that there is no possibility of overflowing a 64KB boundary moving MAX_CNT ;sectors.
MOV AX,PARA_SIZE ;16
MUL RH.RH4_DTAS ;* segment of caller's DTA in DX,AX
ADD AX,RH.RH4_DTAO ;+ offset of caller's DTA
ADC DL,0 ;carry in from addition
DIV CS:WPARA_SIZE ;AX is segment of caller's DTA
;DX is smallest offset possible
;AX:DX = DTA address
;AX:DX is caller's DTA segment:offset, CX:SI is VDISK buffer segment:offset
;If this is an OUTPUT request, exchange the source and target addresses
CMP RH.RHC_CMD,CMD_INPUT ;INPUT operation?
JE INOUT_B ;jump if INPUT operation
XCHG AX,CX ;swap source and target segment
XCHG DX,SI ;swap source and target offset
INOUT_B: ;CX:SI is source, AX:DX is target
MOV CS:IO_SRCS,CX ;save source segment
MOV CS:IO_SRCO,SI ;save source offset
MOV CS:IO_TGTS,AX ;save target segment
MOV CS:IO_TGTO,DX ;save target offset
JMP SHORT INOUT_E ;AX := SECT_LEFT, test for zero INOUT_C: ;SECT_LEFT in AX, non-zero
; Compute number of sectors to transfer in a single move,
; AX = minimum of (SECT_LEFT, MAX_CNT)
; MAX_CNT is the maximum number of sectors that can be moved without
; spanning a 64KB boundary (0FFFFH / Sector size, remainder truncated)
MOV CX,CS:MAX_CNT ;MAX sectors with one move
CMP AX,CX ;if SECT_LEFT cannot span 64KB boundary
JBE INOUT_D ;then move SECT_LEFT sectors
MOV AX,CX ;else move MAX_CNT sectors
INOUT_D:
SUB CS:SECT_LEFT,AX ;reduce number of sectors left to move
;Move AX sectors from source to target
MUL CS:BPB_SSZ ;sectors * sector size = byte count
;(cannot overflow into DX)
SHR AX,1 ;/2 = word count
MOV CX,AX ;word count to CX for REP MOVSW
LDS SI,CS:IO_SRCA ;source segment/offset to DS:SI
LES DI,CS:IO_TGTA ;target segment/offset to ES:DI
REP MOVSW ;move MOV_CNT sectors
;Update source and target paragraph addresses
;AX has number of words moved
SHR AX,1 ;words moved / 8 = paragraphs moved
SHR AX,1
SHR AX,1
ADD CS:IO_SRCS,AX ;add paragraphs moved to source segment
ADD CS:IO_TGTS,AX ;add paragraphs moved to target segment
;Determine if more sectors need to be transferred
INOUT_E: ;do while SECT_LEFT <> zero
MOV AX,CS:SECT_LEFT ;get sectors left to transfer
OR AX,AX ;set flags
JNZ INOUT_C ;go back to transfer some sectors
RET ;AX = zero, all sectors transferred
SUBTTL Extended Memory I/O routine
PAGE ;-----------------------------------------------------------------------;
; Extended Memory I/O routine ; ;-----------------------------------------------------------------------; INOUT_EM: ;Extended memory I/O routine
;change to larger stack
MOV SI,SS ;save old SS in SI
MOV DX,SP ;save old SP in DX
CLI ;disable interrupts
MOV AX,CS
MOV SS,AX ;set SS = CS
MOV SP,OFFSET EM_STACK ;point to new stack
STI ;enable interrupts
PUSH SI ;save old SS at top of new stack
PUSH DX ;save old SP on new stack
MOV SI,RH.RH4_DTAO ;caller's DTA offset
;Compute 24-bit address of VDISK sector in CX (hi) and SI (low)
MOV AX,RH.RH4_SSN ;starting sector number
MUL CS:BPB_SSZ ;* sector size = offset within buffer
ADD AX,CS:START_EM_LO ;+ base address of this VDISK buffer
ADC DL,CS:START_EM_HI
MOV CX,DX ;save high byte
MOV SI,AX ;save low word
;Compute 24-bit address of caller's DTA in DX (hi) and AX (low)
MOV AX,PARA_SIZE ;16
MUL RH.RH4_DTAS ;* segment of caller's DTA
ADD AX,RH.RH4_DTAO ;+ offset of caller's DTA
ADC DL,0 ;carry in from addition
;Caller's DTA address is in CX,SI, VDISK buffer address is in DX,AX.
;If this is an OUTPUT request, exchange the source and target addresses
CMP RH.RHC_CMD,CMD_INPUT ;INPUT operation?
JE INOUT_EM_B ;jump if INPUT operation
XCHG DX,CX ;swap source and target high byte
XCHG AX,SI ;swap source and target low word
INOUT_EM_B: ;CX,SI is source, DX,AX is target
MOV SRC.DESC_BASEL,SI ;low 16 bits of source address
MOV SRC.DESC_BASEH,CL ;high 8 bits of source address
MOV TGT.DESC_BASEL,AX ;low 16 bits of target address
MOV TGT.DESC_BASEH,DL ;high 8 bits of target address
JMP SHORT INOUT_EM_E ;AX := SECT_LEFT, test for zero INOUT_EM_C: ;SECT_LEFT in AX, non-zero
; Compute number of sectors to transfer in a single move,
; AX = minimum of (SECT_LEFT, MAX_CNT)
; MAX_CNT is the maximum number of sectors that can be moved without
; spanning a 64KB boundary (0FFFFH / Sector size, remainder truncated)
MOV CX,CS:MAX_CNT ;MAX sectors with one move
CMP AX,CX ;if SECT_LEFT cannot span 64KB boundary
JBE INOUT_EM_D ;then move SECT_LEFT sectors
MOV AX,CX ;else move MAX_CNT sectors
INOUT_EM_D:
SUB CS:SECT_LEFT,AX ;reduce number of sectors left to move
;Move AX sectors from source to target
MUL CS:BPB_SSZ ;sectors * sector size = byte count
;(cannot overflow into DX)
MOV TGT.DESC_LMT,AX ;store segment limit (byte count)
MOV SRC.DESC_LMT,AX
PUSH AX ;preserve byte count on stack
SHR AX,1 ;/2 = word count
MOV CX,AX ;word count to CX
PUSH CS
POP ES ;set ES = CS
MOV SI,OFFSET GDT ;ES:SI point to GDT
MOV AH,EM_BLKMOVE ;function is block move
INT EM_INT ;move an even number of words
POP CX ;get byte count back from stack
OR AH,AH ;get error code
JNZ INOUT_EM_XE ;jump if I/O error encountered
;Update source and target addresses
ADD SRC.DESC_BASEL,CX ;add bytes moved to source
ADC SRC.DESC_BASEH,0 ;pick up any carry
ADD TGT.DESC_BASEL,CX ;add bytes moved to target
ADC TGT.DESC_BASEH,0 ;pick up any carry
;Determine if more sectors need to be transferred
INOUT_EM_E: ;do while SECT_LEFT <> zero
MOV AX,CS:SECT_LEFT ;get sectors left to transfer
OR AX,AX ;set flags
JNZ INOUT_EM_C ;go back to transfer some sectors
INOUT_EM_X2: ;revert to original stack
POP DI ;get old SP
POP SI ;get old SS
CLI ;disable interrupts
MOV SS,SI ;restore old SS
MOV SP,DI ;restore old SP
STI ;enable interrupts
RET ;return to IRPT_EXIT
INOUT_EM_XE: ;some error with INT 15H
MOV CS:EM_STAT,AX ;save error status for debugging
MOV RH.RH4_CNT,0 ;indicate no sectors transferred
MOV AX,STAT_CRC ;indicate CRC error
JMP INOUT_EM_X2 ;fix stack and exit
INOUT ENDP
DW 40 DUP (?) ;stack for extended memory I/O
EM_STACK LABEL WORD
SUBTTL Boot Record
PAGE ;-----------------------------------------------------------------------;
; Adjust the assembly-time instruction counter to a paragraph ;
; boundary ; ;-----------------------------------------------------------------------;
IF ($-START) MOD 16
ORG ($-START) + 16 - (($-START) MOD 16)
ENDIF
VDISK EQU $ ;start of virtual disk buffer
VDISKP EQU ($-START) / PARA_SIZE ;length of program in paragraphs ;-----------------------------------------------------------------------;
; If this VDISK is in extended memory, this address is passed ;
; back to DOS as the end address that is to remain resident. ;
; ;
; It this VDISK is not in extended memory, the VDISK buffer ;
; begins at this address, and the address passed back to DOS ;
; as the end address that is to remain resident is this address ;
; plus the length of the VDISK buffer. ; ;-----------------------------------------------------------------------;
BOOT_RECORD LABEL BYTE ;Format of Boot Record documented in
;DOS Technical Reference Manual
DB 0,0,0 ;3-byte jump to boot code (not bootable)
DB 'VDISK ' ;8-byte vendor identification
BOOT_BPB LABEL BYTE ;boot record copy of BIOS parameter block
DW ? ;number of bytes per disk sector
DB ? ;sectors per allocation unit
DW ? ;number of reserved sectors (for boot record)
DB ? ;number of File Allocation Table (FAT) copies
DW ? ;number of root directory entries
DW ? ;total number of sectors
DB ? ;media descriptor byte
DW ? ;number of sectors occupied by a single FAT ;end of boot record BIOS Parameter block
;The following three words mean nothing to VDISK, they are placed here
;to conform to the DOS standard for boot records.
DW 8 ;sectors per track
DW 1 ;number of heads
DW 0 ;number of hidden sectors
;The following word is the 16-bit kilobyte address of the first byte in ;extended memory that is not occupied by a VDISK buffer
;It is placed into this location so that other users of extended memory
;may find where all the VDISKs end.
;This field may be accessed by moving the boot record of the First extended ;memory VDISK from absolute location 10 0000H. Before assuming that the
;value below is valid, the vendor ID (constant VDISK) should be verified
;to make sure that SOME VDISK has been installed.
;For example, if two VDISKs are installed, one 320KB and one 64KB, the
;address calculations are as follows:
;Extended memory start address = 100000H (1024KB)
;Start addr of 1st VDISK buffer = 100000H (1024KB)
;Length of 1st VDISK buffer = 050000H ( 320KB)
;End addr of 1st VDISK buffer = 14FFFFH
;Start addr of 2nd VDISK buffer = 150000H (1344KB)
;Length of 2nd VDISK buffer = 010000H ( 64KB)
;End addr of 2nd VDISK buffer = 15FFFFH
;First byte after all VDISKs = 160000H (1408KB)
;Divide by 1024 = 0580H (1408D)
;Content of BOOT_EM = 0580H
BOOT_EM_OFF EQU $-BOOT_RECORD ;offset from 10 0000H of the following word BOOT_EM DW 1024 ;KB addr of first free byte of extended memory ;-----------------------------------------------------------------------;
; Part 2 of Initialization (executed last) ; ;-----------------------------------------------------------------------; ;Initialization is divided into two parts.
;INIT_P1 is overlaid by the virtual disk buffer
;INIT_P1 is executed first, then jumps to INIT_P2. INIT_P2 returns to caller.
;Exercise caution if extending the initialization part 2 code.
;It overlays the area immediately following the boot sector.
;If this section of code must be expanded, make sure it fits into the minimum ;sector size of 128 bytes.
;Label TEST_LENGTH must equate to a non-negative value (TEST_LENGTH >= 0).
;If this code it must be extended beyond the 128 byte length of the boot sector,
;move all of INIT_P2 before label VDISK.
;Registers at entry to INIT_P2 (set up at end of INIT_P1):
; BL = media control byte from BPB (for FAT)
; CX = number of FAT copies
; DX = number of bytes in one FAT - 3
; SI = OFFSET of Volume Label field
; ES:DI = VDISK buffer address of first FAT sector
; CS = DS = VDISK code segment
INIT_P2 PROC ;second part of initialization
ASSUME DS:CSEG ;DS set in INIT_P1
;Initialize File Allocation Table(s) (FATs)
INIT_P2_FAT: ;set up one FAT, sector number in AX
PUSH CX ;save loop counter on stack
MOV AL,BL ;media control byte
STOSB ;store media control byte, increment DI
MOV AX,0FFFFH ;bytes 2 and 3 of FAT are 0FFH
STOSW
MOV CX,DX ;FAT size in bytes - 3
XOR AX,AX ;value to store in remainder of FAT
REP STOSB ;clear remainder of FAT
POP CX ;get loop counter off stack
LOOP INIT_P2_FAT ;loop for all copies of the FAT
;Put the volume label in the first directory entry
MOV CX,VOL_LABEL_LEN ;length of volume directory entry
REP MOVSB ;move volume id to directory
;Zero the remainder of the directory
MOV AX,DIR_ENTRY_SIZE ;length of 1 directory entry
MUL BPB_DIRN ;* number entries = bytes of directory
SUB AX,VOL_LABEL_LEN ;less length of volume label
MOV CX,AX ;length of rest of directory
XOR AX,AX
REP STOSB ;clear directory to nulls
RET ;return with AX=0
INIT_P2 ENDP
PATCH_AREA DB 5 DUP ('PATCH AREA ')
TEST_LENGTH EQU 128-($-VDISK) ;if negative, boot record has too much
;data area, move some fields below VDISK
;-----------------------------------------------------------------------;
; All fields that must remain resident after device driver ;
; initialization must be defined before this point. ; ;-----------------------------------------------------------------------;
DB 'IBM DOS Version 4.00, Virtual Disk Device Driver'
DB '(C)Copyright IBM Corp 1984,1985,1988'
[continued in next message]
--- SoupGate-Win32 v1.05
* Origin: fsxNet Usenet Gateway (21:1/5)