From 2d04cacc5322951f187bb17e017c12920ac8ebe2 Mon Sep 17 00:00:00 2001 From: Mark Zbikowski Date: Thu, 25 Apr 2024 21:24:10 +0100 Subject: MZ is back! --- v4.0/src/DEV/VDISK/MAKEFILE | 33 + v4.0/src/DEV/VDISK/SLM.INI | 4 + v4.0/src/DEV/VDISK/VDISK.ASM | 2221 ++++++++++++++++++++++++++++ v4.0/src/DEV/VDISK/VDISK.INC | 28 + v4.0/src/DEV/VDISK/VDISK.LNK | 3 + v4.0/src/DEV/VDISK/VDISK.SKL | 36 + v4.0/src/DEV/VDISK/VDISKMSG.ASM | 27 + v4.0/src/DEV/VDISK/VDISKSYS.ASM | 3024 +++++++++++++++++++++++++++++++++++++++ v4.0/src/DEV/VDISK/VDISKSYS.INC | 37 + 9 files changed, 5413 insertions(+) create mode 100644 v4.0/src/DEV/VDISK/MAKEFILE create mode 100644 v4.0/src/DEV/VDISK/SLM.INI create mode 100644 v4.0/src/DEV/VDISK/VDISK.ASM create mode 100644 v4.0/src/DEV/VDISK/VDISK.INC create mode 100644 v4.0/src/DEV/VDISK/VDISK.LNK create mode 100644 v4.0/src/DEV/VDISK/VDISK.SKL create mode 100644 v4.0/src/DEV/VDISK/VDISKMSG.ASM create mode 100644 v4.0/src/DEV/VDISK/VDISKSYS.ASM create mode 100644 v4.0/src/DEV/VDISK/VDISKSYS.INC (limited to 'v4.0/src/DEV/VDISK') diff --git a/v4.0/src/DEV/VDISK/MAKEFILE b/v4.0/src/DEV/VDISK/MAKEFILE new file mode 100644 index 0000000..cbbf7ed --- /dev/null +++ b/v4.0/src/DEV/VDISK/MAKEFILE @@ -0,0 +1,33 @@ +#************************ Makefile for vdisk ************************** + +inc =..\..\inc +msg =..\..\messages +dos =..\..\dos +hinc =..\..\hinc + +# +####################### Dependencies begin here. ####################### +# + +all: vdisk.sys + +vdisk.ctl: vdisk.skl \ + $(msg)\$(COUNTRY).msg \ + makefile + + +vdisksys.obj: vdisksys.asm \ + vdisksys.inc \ + vdisk.ctl \ + vdisk.cl2 \ + vdisk.cla \ + vdisk.cl1 \ + $(inc)\msgserv.asm \ + $(inc)\sysmsg.inc \ + makefile + +vdisk.sys: vdisksys.obj \ + vdisk.lnk + link @vdisk.lnk + exe2bin vdisk.exe vdisk.sys + del vdisk.exe diff --git a/v4.0/src/DEV/VDISK/SLM.INI b/v4.0/src/DEV/VDISK/SLM.INI new file mode 100644 index 0000000..be11bcd --- /dev/null +++ b/v4.0/src/DEV/VDISK/SLM.INI @@ -0,0 +1,4 @@ +project = 340 +slm root = //WINSRC/CORE +user root = //D:WINBLD1-D/MSDOS/340/SRC +sub dir = /dev/VDISK diff --git a/v4.0/src/DEV/VDISK/VDISK.ASM b/v4.0/src/DEV/VDISK/VDISK.ASM new file mode 100644 index 0000000..5994111 --- /dev/null +++ b/v4.0/src/DEV/VDISK/VDISK.ASM @@ -0,0 +1,2221 @@ + 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 1988 Microsoft +;Licensed Material - Program Property of Microsoft + +;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 Revised + 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 'MS DOS Version 4.00 - Virtual Disk Device Driver' + DB '-------- Licensed Material ---------' + DB 'Program Property of Microsoft Corporation. ' + DB '(C)Copyright 1988 Microsoft' + DB 'Thank You For Your ' + DB ' Support ' + +MAXSEC_TRF DW 0 ;maximum number of sectors to transfer when + ;in extended memory + +BUFF_SIZE DW 0 ;desired VDISK buffer size in kilobytes + +MIN_MEMORY_LEFT DW 64 ;minimum amount of system memory (kilobytes) + ;that must remain after VDISK is installed + +FIRST_EM_SW DB ? ;0FFH if this is the first device driver + ;to be installed in extended memory + ;00H if another VDISK extended memory driver + ;has been installed + +FIRST_VDISK DW ? ;segment address of 1st VDISK device driver +PARA_PER_KB DW 1024/PARA_SIZE ;paragraphs in one kilobyte +C1024 DW 1024 ;bytes in one kilobyte +DIRE_SIZE DW DIR_ENTRY_SIZE ;bytes in one directory entry +DIR_SECTORS DW ? ;number of sectors of directory + +ERR_FLAG DB 0 ;error indicators to condition messages +ERR_BSIZE EQU 80H ;buffer size adjusted +ERR_SSZ EQU 40H ;sector size adjusted +ERR_DIRN EQU 20H ;number of directory entries adjusted +ERR_PASS EQU 10H ;some adjustment made that requires + ;recomputation of values previously computed +ERR_SSZB EQU ERR_SSZ+ERR_PASS ;sector size altered this pass +ERR_SYSSZ EQU 08H ;system storage too small for VDISK +ERR_SWTCH EQU 04H ;invalid switch character +ERR_EXTSW EQU 02H ;extender card switches don't match memory size +ERR_ESIZE EQU 01H ;Transfer size adjusted + +; additional errors added - kwc + +major_version equ 4 ;Major DOS version +minor_version equ 00 ;Minor DOS Version + +expected_version equ (MINOR_VERSION SHL 8)+MAJOR_VERSION + +err_flag2 db 0 +err_baddos equ 01h ; Invalid DOS Version + + SUBTTL Initialization, Part one + PAGE +;-----------------------------------------------------------------------; +; Command Code 0 - Initialization ; +; At entry, DS:BX point to request header, AX = 0 ; +;-----------------------------------------------------------------------; +;Initialization is divided into two parts. +;This part, executed first, is later overlaid by the VDISK buffer. + +INIT_P1 PROC ;first part of initialization + MOV DX,SS ;save stack segment register + MOV CX,SP ;save stack pointer register + CLI ;inhibit interrupts while changing SS:SP + MOV AX,CS ;move CS to SS through AX + MOV SS,AX + MOV SP,OFFSET MSGEND ;end of VDISKMSG + ADD SP,STACK_SIZE ;+ length of our stack + STI ;allow interrupts + PUSH DX ;save old SS register on new stack + PUSH CX ;save old SP register on new stack + + push bx ;secure registers before DOS int + push cx ;secure registers before DOS int + +; add version check - kwc + + mov ah,030h + int 21h + pop cx ;restore pointer values + pop bx ;restore pointer values + cmp ax,expected_version + je okdos + + or cs:err_flag2,err_baddos + +okdos: + CALL GET_PARMS ;get parameters from CONFIG.SYS line + + PUSH CS + POP DS ;set DS = CS + ASSUME DS:CSEG + + CALL APPLY_DEFAULTS ;supply any values not specified + CALL DETERMINE_START ;compute start address of VDISK buffer + CALL VALIDATE ;validate parameters + CALL COPY_BPB ;Copy BIOS Parameter Block to boot record + + CALL VERIFY_EXTENDER ;Verify that extender card switches are right + + TEST ERR_FLAG,ERR_EXTSW ;are switches wrong? + JNZ INIT_P1_A ;if so, exit with messages + + test CS:err_flag2,err_baddos + jnz init_p1_a + + CMP EM_SW,0 ;extended memory requested? + JE INIT_P1_A ;jump if not + + TEST ERR_FLAG,ERR_SYSSZ ;is system too small for VDISK? + JNZ INIT_P1_A ;if so, don't do extended memory init + + CALL UPDATE_AVAIL ;update AVAIL_HI and AVAIL_LO to reflect + ;addition of extended memory VDISK + CALL FORMAT_VDISK ;construct a boot record, FATs and + ;directory in storage immediately + ;following this device driver + CALL MOVE_VDISK ;move formatted boot record, FATs, + ;and directory to extended memory + CALL UPDATE_BOOT ;place the end address of ALL VDISKs + ;in the boot record of the first VDISK + CMP FIRST_EM_SW,0 ;is this the first extended memory VDISK? + JE INIT_P1_A ;no, exit + + CALL STEAL_INT19 ;point INT 19H to this VDISK +INIT_P1_A: + CALL FILL_RH ;fill in INIT request header + CALL WRITE_MESSAGES ;display all messages + POP CX ;get old SP from stack + POP DX ;get old SS from stack + CLI ;disable interrupts while changing SS:SP + MOV SS,DX ;restore stack segment register + MOV SP,CX ;restore stack pointer register + STI ;enable interrupts +;-----------------------------------------------------------------------; +; INIT_P2 must be short enough to fit into the boot sector ; +; (minimum size of boot sector is 128 bytes), so we set up ; +; as many pointers as we can to help keep INIT_P2 short. ; +; ; +; ES:DI = storage address of first FAT sector ; +; BL = media control byte ; +; CX = number of FAT copies ; +; DX = number of bytes in one FAT, less 3 ; +; SI = offset of VOL label field ; +;-----------------------------------------------------------------------; + MOV ES,START_BUFFER_PARA ;start paragraph of VDISK buffer + + MOV AX,BPB_RES ;number of reserved sectors + MUL BPB_SSZ ;* sector size + MOV DI,AX ;ES:DI point to FAT start + + MOV BL,BPB_MCB ;media control byte + + MOV CL,BPB_FATN ;number of FAT copies + XOR CH,CH + + MOV AX,BPB_FATSZ ;FAT size in sectors + MUL BPB_SSZ ;* sector size = total FAT bytes + + SUB AX,3 ;-3 (FEFFFF stored by code) + MOV DX,AX + + MOV SI,OFFSET VOL_LABEL ;point to VOL label directory entry + JMP INIT_P2 ;jump to second part of initialization + ;this is redundant if the VDISK is in + ;extended memory, but is executed anyway + + SUBTTL GET_PARMS Parameter Line Scan + PAGE +;-----------------------------------------------------------------------; +;GET_PARMS gets the parameters from the CONFIG.SYS statement ; +; ; +;Register usage: ; +; DS:SI indexes parameter string ; +; AL contains character from parameter string ; +; CX value from GET_NUMBER ; +;-----------------------------------------------------------------------; + ASSUME DS:NOTHING ;DS:BX point to Request Header +GET_PARMS PROC ;get parameters from CONFIG.SYS line + PUSH DS ;save DS + LDS SI,RH.RH0_BPBA ;DS:SI point to all after DEVICE= + ;in CONFIG.SYS line + XOR AL,AL ;not at end of line + +;Skip until first delimiter is found. There may be digits in the path string. + +;DS:SI points to \pathstring\VDISK.SYS nn nn nn +;The character following VDISK.SYS may have been changed to a null (00H). +;All letters have been changed to uppercase. + +GET_PARMS_A: ;skip to DOS delimiter character + CALL GET_PCHAR ;get parameter character into AL + JZ GET_PARMS_X ;get out if end of line encountered + OR AL,AL ;test for null + JZ GET_PARMS_C ; + CMP AL,' ' + JE GET_PARMS_C ; + CMP AL,',' + JE GET_PARMS_C ; + CMP AL,';' + JE GET_PARMS_C ; + CMP AL,'+' + JE GET_PARMS_C ; + CMP AL,'=' + JE GET_PARMS_C ; + CMP AL,TAB + JNE GET_PARMS_A ;skip until delimiter or CR + + + +GET_PARMS_C: + PUSH SI ;save to rescan + MOV CS:EM_SW,0 ;indicate no /E found + JMP GET_SLASH ;see if current character is an slash + +GET_PARMS_D: ;scan for / + CALL GET_PCHAR + JZ GET_PARMS_B ;exit if end of line + +GET_SLASH: ;check for slash + CMP AL,'/' ;found slash? + JNE GET_PARMS_D ;no, continue scan + + CALL GET_PCHAR ;get char following slash + CMP AL,'E' ;don't have to test for lower case E, + ;letters have been changed to upper case + JNE GET_PARMS_E ;not 'E' + MOV CS:EM_SW,AL ;indicate /E found + + CALL GET_PCHAR ;get char following E + CMP AL,':' ;is it a delimeter ? + JNE GET_PARMS_D ;not a ':' + + + CALL GET_MAXSIZE ;get maximum sector size + + + JMP GET_PARMS_D ;continue forward scan + +GET_PARMS_E: ;/ found, not 'E' + OR CS:ERR_FLAG,ERR_SWTCH ;indicate invalid switch character + JMP GET_PARMS_D ;continue scan + + + +GET_PARMS_B: ;now pointing to first delimiter + POP SI ;get pointer, used to rescan for /E + XOR AL,AL ;not at EOL now + CALL GET_PCHAR ;get first character + CALL SKIP_TO_DIGIT ;skip to first digit + JZ GET_PARMS_X ;found EOL, no digits remain + + CALL GET_NUMBER ;extract digits, convert to binary + MOV CS:BUFF_SIZE,CX ;store buffer size + + CALL SKIP_TO_DIGIT ;skip to next digit + JZ GET_PARMS_X ;found EOL, no digits remain + + CALL GET_NUMBER ;extract digits, convert to binary + MOV CS:BPB_SSZ,CX ;store sector size + + CALL SKIP_TO_DIGIT ;skip to next digit + JZ GET_PARMS_X ;found EOL, no digits remain + + CALL GET_NUMBER ;extract digits, convert to binary + MOV CS:BPB_DIRN,CX ;store number of directory entries + + + +GET_PARMS_X: ;premature end of line + POP DS ;restore DS + RET + + + +GET_MAXSIZE PROC ;get maximum sector size + + CALL GET_PCHAR ;get next character + CALL CHECK_NUM ;is it a number ? + JZ GET_NEXTNUM ;yes, go get next number + OR CS:ERR_FLAG,ERR_ESIZE ;indicate invalid sector size + RET ; +GET_NEXTNUM: ;get next number + CALL GET_NUMBER ;extract digits and convert to binary + MOV CS:MAXSEC_TRF,CX ;save maximum sector size to transfer + RET +GET_MAXSIZE ENDP + + + +GET_PCHAR PROC ;internal proc to get next character into AL + CMP AL,CR ;carriage return already encountered? + JE GET_PCHAR_X ;don't read past end of line + CMP AL,LF ;line feed already encountered? + JE GET_PCHAR_X ;don't read past end of line + LODSB ;get char from DS:SI, increment SI + CMP AL,CR ;is the char a carriage return? + JE GET_PCHAR_X ;yes, set Z flag at end of line + CMP AL,LF ;no, is it a line feed? +GET_PCHAR_X: ;attempted read past end of line + RET +GET_PCHAR ENDP ;returns char in AL + + +CHECK_NUM PROC ;check AL for ASCII digit + CMP AL,'0' ;< '0'? + JB CHECK_NUM_X ;exit if it is + + CMP AL,'9' ;> '9'? + JA CHECK_NUM_X ;exit if it is + + CMP AL,AL ;set Z flag to indicate numeric +CHECK_NUM_X: + RET ;Z set if numeric, NZ if not numeric +CHECK_NUM ENDP + + +SKIP_TO_DIGIT PROC ;skip to first numeric character + CALL CHECK_NUM ;is current char a digit? + JZ SKIP_TO_DIGIT_X ;if so, skip is complete + + CALL GET_PCHAR ;get next character from line + JNZ SKIP_TO_DIGIT ;loop until first digit or CR or LF + RET ;character is CR or LF + +SKIP_TO_DIGIT_X: + CMP AL,0 ;digit found, force NZ + RET +SKIP_TO_DIGIT ENDP + +C10 DW 10 +GN_ERR DB ? ;zero if no overflow in accumulation + +GET_NUMBER PROC ;convert string of digits to binary value + XOR CX,CX ;accumulate number in CX + MOV CS:GN_ERR,CL ;no overflow yet +GET_NUMBER_A: ;accumulate next digit + SUB AL,'0' ;convert ASCII to binary + CBW ;clear AH + XCHG AX,CX ;previous accumulation in AX, new digit in CL + MUL CS:C10 ;DX:AX := AX*10 + OR CS:GN_ERR,DL ;set GN_ERR <> 0 if overflow + ADD AX,CX ;add new digit from + XCHG AX,CX ;number now in CX + DEC SI ;back up to prior entry + MOV AL,' ' ;blank out prior entry + MOV [SI],AL ; + INC SI ;set to current entry + CALL GET_PCHAR ;get next character + CALL CHECK_NUM ;see if it was numeric + JZ GET_NUMBER_A ;continue accumulating + CMP CS:GN_ERR,0 ;did we overflow? + JE GET_NUMBER_B ;if not, we're done + XOR CX,CX ;return zero (always invalid) if overflow +GET_NUMBER_B: + RET ;number in CX, next char in AL +GET_NUMBER ENDP + +GET_PARMS ENDP + + SUBTTL APPLY_DEFAULTS + PAGE +;-----------------------------------------------------------------------; +; APPLY_DEFAULTS supplies any parameter values that the user ; +; failed to specify ; +;-----------------------------------------------------------------------; + ASSUME DS:CSEG +APPLY_DEFAULTS PROC + XOR AX,AX + CMP BUFF_SIZE,AX ;is buffer size zero? + JNE APPLY_DEFAULTS_A ;no, user specified something + + MOV BUFF_SIZE,DFLT_BSIZE ;supply default buffer size + OR ERR_FLAG,ERR_BSIZE ;indicate buffersize adjusted + +APPLY_DEFAULTS_A: + CMP BPB_SSZ,AX ;is sector size zero? + JNE APPLY_DEFAULTS_B ;no, user specified something + + MOV BPB_SSZ,DFLT_SSZ ;supply default sector size + OR ERR_FLAG,ERR_SSZ ;indicate sector size adjusted + +APPLY_DEFAULTS_B: + CMP BPB_DIRN,AX ;are directory entries zero? + JNE APPLY_DEFAULTS_C ;no, user specified something + + MOV BPB_DIRN,DFLT_DIRN ;supply default directory entries + OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted + +APPLY_DEFAULTS_C: ; + CMP EM_SW,0 ;extended memory ? + JE APPLY_DEFAULTS_D ;no, jump around + CMP MAXSEC_TRF,AX ;is maximum sectors zero? + JNE APPLY_DEFAULTS_D ;no, user specified something + + MOV MAXSEC_TRF,DFLT_ESS ;supply default maximum number of + ;sector to transfer + OR ERR_FLAG,ERR_ESIZE ;indicate transfer size adjusted +APPLY_DEFAULTS_D: + RET +APPLY_DEFAULTS ENDP + + SUBTTL DETERMINE_START address of VDISK buffer + PAGE +;-----------------------------------------------------------------------; +; DETERMINE_START figures out the starting address of the VDISK ; +; buffer ; +;-----------------------------------------------------------------------; + ASSUME DS:CSEG +DETERMINE_START PROC + +;If extended memory is NOT being used, the VDISK buffer immediately +;follows the resident code. + +;If extended memory IS being used, START_BUFFER_PARA becomes the +;end of device driver address passed back to DOS. + + MOV AX,CS ;start para of VDISK code + ADD AX,VDISKP ;+ length of resident code + MOV START_BUFFER_PARA,AX ;save as buffer start para + + CMP EM_SW,0 ;is extended memory requested? + JE DETERMINE_START_X ;if not, we're done here + +;If this is the first extended memory VDISK device driver to be installed, +;the start address for I/O is 1 megabyte. + +;If one or more extended memory VDISK device drivers have been installed, +;the start address for I/O for THIS device driver is acquired from the +;fields AVAIL_LO and AVAIL_HI in the FIRST VDISK device driver. + +;The first extended memory VDISK device driver is located by INT 19H's vector. + + MOV FIRST_EM_SW,0FFH ;indicate first VDISK device driver + MOV FIRST_VDISK,CS ;segment addr of first VDISK + + PUSH DS ;preserve DS + XOR AX,AX + MOV DS,AX ;set DS = 0 + ASSUME DS:INT_VEC + + MOV AX,DS:BOOT_VECS ;get segment addr of INT 19H routine + MOV DS,AX ;to DS + ASSUME DS:NOTHING + + PUSH CS + POP ES ;set ES = CS + MOV SI,OFFSET VOL_LABEL ;DS:SI point to VOL label field + ;in first VDISK (if present) + MOV DI,SI ;ES:DI point to VOL label field of + ;this VDISK + + MOV CX,VOL_LABEL_LEN ;length of volume label + REP CMPSB ;does INT 19H vector point to a VDISK + ;device driver? + JNE DETERMINE_START_A ;jump if this is the first VDISK + +;Another extended memory VDISK device driver has been installed. +;Its AVAIL_LO and AVAIL_HI are the first free byte of extended memory. + + MOV CS:FIRST_EM_SW,0 ;indicate not first device driver + MOV CS:FIRST_VDISK,DS ;save pointer to 1st device driver + +;Copy AVAIL_LO and AVAIL_HI from first VDISK to this VDISK + + MOV SI,OFFSET AVAIL_LO ;DS:SI point to AVAIL_LO in first VDISK + MOV DI,SI ;ES:DI point to AVAIL_LO in this VDISK + MOVSW ;copy AVAIL_LO from first to this VDISK + MOVSB ;copy AVAIL_HI + +DETERMINE_START_A: ;copy AVAIL_LO and AVAIL_HI to START_EM + POP DS ;set DS = CS + + MOV SI,OFFSET AVAIL_LO ;source offset + MOV DI,OFFSET START_EM_LO ;destination offset + + MOVSW ;move AVAIL_LO to START_EM_LO + MOVSB ;move AVAIL_HI to START_EM_HI +DETERMINE_START_X: + RET +DETERMINE_START ENDP + + SUBTTL VALIDATE parameters + PAGE +;-----------------------------------------------------------------------; +; VALIDATE adjusts parameters as necessary ; +;-----------------------------------------------------------------------; +VAL_SSZ_TBL LABEL WORD ;table of valid sector sizes +VAL_SSZ_S DW 128 ;smallest valid sector size + DW 256 +VAL_SSZ_L DW 512 ;largest valid sector size +VAL_SSZ_N EQU ($-VAL_SSZ_TBL)/2 ;number of table entries + + ASSUME DS:CSEG +VALIDATE PROC ;validate parameters + MOV BPB_AUSZ,1 ;initial allocation unit is 1 sector + + CALL VAL_BSIZE ;validate buffer size + + CALL VAL_SSZ ;validate (adjust if necessary) BPB_SSZ + +VALIDATE_A: + AND ERR_FLAG,255-ERR_PASS ;indicate nothing changed this pass + + MOV AX,BPB_SSZ ;sector size + CWD ;clear DX for division + DIV WPARA_SIZE ;sector size/para size + MOV PARAS_PER_SECTOR,AX ;number of paragraphs/sector + + MOV AX,BUFF_SIZE ;requested buffersize in KB + MUL C1024 ;DX:AX = buffer size in bytes + DIV BPB_SSZ ;/sector size = # sectors + MOV BPB_SECN,AX ;store number of sectors + + CALL VAL_DIRN ;validate number of directory entries + + TEST ERR_FLAG,ERR_PASS ;may have reset sector size + JNZ VALIDATE_A ;recompute directory & FAT sizes + + CALL VAL_FAT ;compute FAT entries, validity test + + TEST ERR_FLAG,ERR_PASS ;if cluster size altered this pass + JNZ VALIDATE_A ;recompute directory & FAT sizes + +;Make certain buffer size is large enough to contain: +; boot sector(s) +; FAT sector(s) +; directory sector(s) +; at least 1 data cluster + + MOV AL,BPB_FATN ;number of FAT copies + CBW ;clear AH + MUL BPB_FATSZ ;* sectors for 1 FAT = FAT sectors + ADD AX,BPB_RES ;+ reserved sectors + ADD AX,DIR_SECTORS ;+ directory sectors + MOV CL,BPB_AUSZ ;get sectors/cluster + XOR CH,CH ;CX = sectors in one cluster + ADD AX,CX ;+ one data cluster + CMP BPB_SECN,AX ;compare with sectors available + JAE VALIDATE_X ;jump if enough sectors + + CMP DIR_SECTORS,1 ;down to 1 directory sector? + JBE VALIDATE_C ;can't let it go below 1 + + MOV AX,BPB_SSZ ;sector size + CWD ;clear DX for division + DIV DIRE_SIZE ;sectorsize/dir entry size = entries/sector + SUB BPB_DIRN,AX ;reduce directory entries by 1 sector + + OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted + JMP VALIDATE_A ;retry with new directory entries number + +VALIDATE_C: ;not enough space for any VDISK + OR ERR_FLAG,ERR_SYSSZ +VALIDATE_X: + RET + + SUBTTL VAL_BSIZE Validate buffer size + PAGE +;-----------------------------------------------------------------------; +; VAL_BSIZE adjusts the buffer size as necessary ; +;-----------------------------------------------------------------------; +VAL_BSIZE PROC + CALL GET_MSIZE ;determine memory available to VDISK + ;returns available KB in AX + OR AX,AX ;is any memory available at all? + JNZ VAL_BSIZE_B ;yes, continue + + OR ERR_FLAG,ERR_SYSSZ ;indicate system too small for VDISK + MOV BUFF_SIZE,1 ;set up minimal values to continue init + MOV AX,VAL_SSZ_S ;smallest possible sector size + MOV BPB_SSZ,AX + MOV BPB_DIRN,4 ;4 directory entries + RET + +VAL_BSIZE_B: ;some memory is available + CMP AX,BUFF_SIZE ;is available memory >= requested? + JAE VAL_BSIZE_C ;if so, we're done + + MOV BUFF_SIZE,AX ;give all available memory + OR ERR_FLAG,ERR_BSIZE ;indicate buffersize adjusted +VAL_BSIZE_C: + RET + + +GET_MSIZE PROC ;determine memory available to VDISK + ;returns KB available in AX + CMP EM_SW,0 ;extended memory? + JE GET_MSIZE_2 ;use non-extended memory routine + + MOV AH,EM_MEMSIZE ;function code to AH + INT EM_INT ;get extended memory size in AX + JC GET_MSIZE_Z ;if error, no extended memory installed + + MUL C1024 ;DX,AX = bytes of extended memory + ADD DX,10H ;DX,AX = high addr of extended memory+1 + SUB AX,AVAIL_LO ;- address of first available byte + SBB DL,AVAIL_HI ;is number of free bytes + DIV C1024 ;AX = number of whole free kilobytes + RET + +GET_MSIZE_2: ;non-extended memory size determination + +;Compute AX = total system size, - (VDISK end address + 64KB) + + MOV AX,START_BUFFER_PARA ;paragraph end of VDISK code + XOR DX,DX ;clear for division + DIV PARA_PER_KB ;KB address of load point + ADD DX,0FFFFH ;round upward to KB boundary + ADC AX,MIN_MEMORY_LEFT ;pick up CY and the 64KB we should leave + PUSH AX ;save across interrupt + INT MEM_SIZE ;get total system size + POP DX ;amount of total that we can't use + SUB AX,DX ;available space to VDISK + JNC GET_MSIZE_X ;exit if positive + +GET_MSIZE_Z: + XOR AX,AX ;indicate no memory available +GET_MSIZE_X: ;exit from memory size determination + RET +GET_MSIZE ENDP + +VAL_BSIZE ENDP + + SUBTTL VAL_SSZ Validate Sector Size + PAGE +;-----------------------------------------------------------------------; +; VAL_SSZ validates sector size, adjusting if necessary ; +;-----------------------------------------------------------------------; +VAL_SSZ PROC ;validate sector size + CMP CS:EM_SW,0 ;extended memory ? + JE VAL_SSZ_ST ;no,go check sector size + MOV BX,MAXSEC_TRF ;move number of sectors to transfer + CMP BX,1 ;> or equal to 1 ? + JB DFLT_TRF ;set default if it is + CMP BX,8 ;> than 8 ? + JA DFLT_TRF ;set default if it is + JMP VAL_SSZ_ST ;continue processing + +DFLT_TRF: ;set default + MOV MAXSEC_TRF,DFLT_ESS ; + MOV BX,MAXSEC_TRF ; + OR CS:ERR_FLAG,ERR_ESIZE ;indicate transfer size adjusted + +VAL_SSZ_ST: ;validate sector size + MOV MAX_CNT,BX ;initialize maximum number of sectors + ;to transfer for extended memory case + MOV BX,BPB_SSZ ;requested sector size + MOV CX,VAL_SSZ_N ;number of table entries + MOV SI,OFFSET VAL_SSZ_TBL ;DS:SI point to table start +VAL_SSZ_A: + LODSW ;get table entry, step table pointer + CMP AX,BX ;is value in table? + JE VAL_SSZ_X ;exit if value found + LOOP VAL_SSZ_A ;loop until table end + + MOV BX,DFLT_SSZ ;get default sector size + MOV BPB_SSZ,BX ;set sector size to default value + OR ERR_FLAG,ERR_SSZ ;indicate sector size adjusted +VAL_SSZ_X: + +;Compute the maximum number of sectors that can be moved in 64KB (less one) +;Restricting moves to this amount avoids 64KB boundary problems. + + CMP CS:EM_SW,0 ;extended memory ? + JNE SIZE_DONE ;yes, we are done + XOR DX,DX + MOV AX,0FFFFH ;64KB - 1 + DIV BX ;/sector size + MOV MAX_CNT,AX ;max sectors in one move +SIZE_DONE: + RET +VAL_SSZ ENDP + + SUBTTL VAL_DIRN Validate number of directory entries + PAGE +;-----------------------------------------------------------------------; +; VAL_DIRN validates and adjusts the number of directory entries. ; +; ; +; Minimum is MIN_DIRN, maximum is MAX_DIRN. If outside these ; +; limits, DFLT_DIRN is used. ; +; ; +; The number of directory entries is rounded upward to fill ; +; a sector ; +;-----------------------------------------------------------------------; +VAL_DIRN PROC + MOV AX,BPB_DIRN ;requested directory entries + CMP AX,MIN_DIRN ;if less than minimum + JB VAL_DIRN_A ;use default instead + + CMP AX,MAX_DIRN ;if <= maximum + JBE VAL_DIRN_B ;accept value as provided + +VAL_DIRN_A: + MOV AX,DFLT_DIRN ;use default directory entries + OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted +VAL_DIRN_B: ;AX is number of directory entries + MUL DIRE_SIZE ;* 32 = bytes of directory requested + DIV BPB_SSZ ;/ sector size = # of directory sectors + OR DX,DX ;test remainder for zero + JZ VAL_DIRN_C ;jump if exact fit + + INC AX ;increment directory sectors + OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted +VAL_DIRN_C: ;make sure enough sectors available + MOV DX,BPB_SECN ;total sectors on media + SUB DX,BPB_RES ;less reserved sectors + SUB DX,2 ;less minimum FAT and 1 data sector + CMP AX,DX ;if directory sectors <= available + JLE VAL_DIRN_D ;use requested amount + + MOV AX,1 ;use only one directory sector + OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted +VAL_DIRN_D: + MOV DIR_SECTORS,AX ;save number of directory sectors + MUL BPB_SSZ ;dir sectors * sector size = dir bytes + DIV DIRE_SIZE ;dir bytes / entry size = entries + MOV BPB_DIRN,AX ;store adjusted directory entries + RET +VAL_DIRN ENDP + + SUBTTL VAL_FAT Validate File Allocation Table (FAT) + PAGE +;-----------------------------------------------------------------------; +;VAL_FAT computes: ; +;BPB_FATSZ, the number of sectors required per FAT copy ; +; ; +;Each FAT entry is 12 bits long, for a maximum of 4095 FAT entries. ; +;(A few FAT entries are reserved, so the highest number of FAT entries ; +;we permit is 0FE0H.) With large buffer sizes and small sector sizes, ; +;we have more allocation units to describe than a 12-bit entry will ; +;describe. If the number of FAT entries is too large, the sector size ; +;is increased (up to a maximum of 512 bytes), and then the allocation ; +;unit (cluster) size is doubled, until we have few enough allocation ; +;units to be properly described in 12 bits. ; +; ; +;This computation is slightly conservative in that the FAT entries ; +;necessary to describe the FAT sectors are included in the computation. ; +;-----------------------------------------------------------------------; +VAL_FAT PROC + MOV AX,BPB_SECN ;total number of sectors + SUB AX,BPB_RES ;don't count boot sector(s) + SUB AX,DIR_SECTORS ;don't count directory sectors + JG VAL_FAT_A ;jump if some remaining + MOV BPB_SSZ,DFLT_SSZ ;force default sector size + OR ERR_FLAG,ERR_SSZ+ERR_PASS ;indicate sector size adjusted + JMP SHORT VAL_FAT_X ;recompute all values +VAL_FAT_A: + XOR DX,DX ;clear DX for division + MOV CL,BPB_AUSZ ;CX = sectors/cluster + XOR CH,CH + DIV CX ;whole number of clusters in AX + ADD DX,0FFFFH ;set carry if remainder + ADC AX,0 ;increment AX if remainder + CMP AX,MAX_FATE ;number of FAT entries too large? + JBE VAL_FAT_C ;no, continue + + MOV AX,BPB_SSZ ;pick up current sector size + CMP AX,VAL_SSZ_L ;already at largest permitted? + JE VAL_FAT_B ;yes, can't make it any larger + + SHL BPB_SSZ,1 ;double sector size + OR ERR_FLAG,ERR_SSZB ;indicate sector size adjusted + JMP SHORT VAL_FAT_X ;recompute all sizes with new BPBSSZ + +VAL_FAT_B: ;sector size is at maximum + SHL BPB_AUSZ,1 ;double allocation unit size + OR ERR_FLAG,ERR_PASS ;indicate another pass required + JMP SHORT VAL_FAT_X ;recompute values + +VAL_FAT_C: ;FAT size = 1.5 * number of clusters + MOV CX,AX ;number of clusters + SHL AX,1 ;* 2 + ADD AX,CX ;* 3 + SHR AX,1 ;* 1.5 + ADC AX,3 ;add 3 bytes for first 2 FAT entries + ;(media descriptor and FFFFH), and CY + XOR DX,DX ;clear DX for division + DIV BPB_SSZ ;FAT size/sector size + ADD DX,0FFFFH ;set carry if remainder + ADC AX,0 ;round upward + MOV BPB_FATSZ,AX ;number of sectors for 1 FAT copy +VAL_FAT_X: + RET +VAL_FAT ENDP + + +VALIDATE ENDP + + SUBTTL COPY_BPB Copy BPB to Boot Record + PAGE +;-----------------------------------------------------------------------; +; COPY_BPB copies the BIOS Parameter Block (BPB) ; +; to the VDISK Boot Record ; +;-----------------------------------------------------------------------; + ASSUME DS:CSEG +COPY_BPB PROC ;Copy BBP to Boot Record + PUSH DS + POP ES ;set ES = DS + + MOV CX,BPB_LEN ;length of BPB + MOV SI,OFFSET BPB ;source offset + MOV DI,OFFSET BOOT_BPB ;target offset + REP MOVSB ;copy BPB to boot record + RET +COPY_BPB ENDP + + SUBTTL VERIFY_EXTENDER + PAGE +;-----------------------------------------------------------------------; +; VERIFY_EXTENDER makes sure that if an Expansion Unit is ; +; installed, the memory size switches on the Extender Card ; +; are correctly set. ; +;-----------------------------------------------------------------------; + + + ASSUME DS:CSEG +EXT_P210 EQU 0210H ;write to latch expansion bus data + ;read to verify expansion bus data +EXT_P213 EQU 0213H ;Expansion Unit status + +VERIFY_EXTENDER PROC + + NOP + + MOV DX,EXT_P210 ;Expansion bus data port address + + MOV AX,5555H ;set data pattern + OUT DX,AL ;write 55H to control port + PUSH DX + POP DX + + JMP SHORT $+2 ;Let the I/O circuits catch up + IN AL,020h ;Clear the CMOS bus drivers! + + IN AL,DX ;recover data + CMP AH,AL ;did we recover the same data? + JNE VERIFY_EXTENDER_X ;if not, no extender card + + NOT AX ;set AX = 0AAAAH + OUT DX,AL ;write 0AAH to control port + PUSH DX ;load data line + POP DX ;load data line + + JMP SHORT $+2 ;Let the I/O circuits catch up + IN AL,020h ;Clear the CMOS bus drivers! + + IN AL,DX ;recover data + CMP AH,AL ;did we recover the same data? + JNE VERIFY_EXTENDER_X ;if not, no extender card + +;Expansion Unit is present. + +;Determine what the switch settings should be on the Extender Card + + INT MEM_SIZE ;get system memory size in KB in AX + ADD AX,63D ;memory size + 63K + MOV CL,6 ;2^6 = 64 + SHR AX,CL ;divide by 64 + ;AX is highest segment address + MOV AH,AL ;save number of segments + +;Read Expander card switch settings + + MOV DX,EXT_P213 ;expansion unit status + IN AL,DX ;read status + ;bits 7-4 (hi nibble) are switches + MOV CL,4 ;shift count + SHR AL,CL ;shift switches to bits 3-0 of AL + + CMP AH,AL ;do switches match memory size? + JE VERIFY_EXTENDER_X ;yes, exit normally + + OR ERR_FLAG,ERR_EXTSW ;indicate switch settings are wrong + +VERIFY_EXTENDER_X: + RET +VERIFY_EXTENDER ENDP + + SUBTTL UPDATE_AVAIL + PAGE +;-----------------------------------------------------------------------; +; UPDATE_AVAIL updates the address of the first byte in extended ; +; memory not used by any VDISK buffer ; +;-----------------------------------------------------------------------; +UPDATE_AVAIL PROC ;update AVAIL_LO and AVAIL_HI of first VDISK + MOV AX,BUFF_SIZE ;number of KB of VDISK buffer + MUL C1024 ;DX,AX = number of bytes of VDISK buffer + + PUSH DS + MOV DS,FIRST_VDISK ;set DS to first VDISK + ADD DS:AVAIL_LO,AX ;update first available byte location + ADC DS:AVAIL_HI,DL + POP DS + RET +UPDATE_AVAIL ENDP + + SUBTTL FORMAT_VDISK + PAGE +;-----------------------------------------------------------------------; +; This Request Header is used by MOVE_VDISK to move the ; +; first few sectors of the virtual disk (boot, FAT, and ; +; Directory) into extended memory. ; +;-----------------------------------------------------------------------; + +MOVE_RH DB MOVE_RH_L ;length of request header + DB 0 ;sub unit + DB 8 ;output operation + DW 0 ;status + DQ ? ;reserved for DOS + DB ? ;media descriptor byte +MOVE_RHO DW ? ;offset of data transfer address +MOVE_RHS DW ? ;segment of data transfer address +MOVE_RHCNT DW ? ;count of sectors to transfer + DW 0 ;starting sector number +MOVE_RH_L EQU $-MOVE_RH ;length of request header + +;-----------------------------------------------------------------------; +; FORMAT_VDISK formats the boot sector, FAT, and directory of an ; +; extended memory VDISK in storage immediately following ; +; VDISK code, in preparation for moving to extended memory. ; +;-----------------------------------------------------------------------; +FORMAT_VDISK PROC ;format boot record, FATs and directory + + MOV AX,CS ;compute 20-bit address + MUL WPARA_SIZE ;16 * segment + ADD AX,OFFSET MSGEND ;+ offset + ADC DL,0 ;pick up carry + ADD AX,STACK_SIZE ;plus stack size + ADC DL,0 ;pick up carry + + DIV WPARA_SIZE ;split into segment(AX)&offset(DX) + MOV MOVE_RHS,AX ;save in Request Header for move + MOV MOVE_RHO,DX + + MOV DI,DX ;offset to DI + MOV ES,AX ;segment to ES + +;copy the boot record + + MOV SI,OFFSET BOOT_RECORD ;point to source field + MOV AX,BPB_RES ;number of reserved sectors + MUL BPB_SSZ ;* sector size = length of boot records + MOV CX,AX ;length to CX for move + REP MOVSB ;move boot record(s) + +;format the FAT(s) + + MOV CL,BPB_FATN ;number of FATs + XOR CH,CH +FORMAT_VDISK_A: ;set up one FAT + PUSH CX ;save loop counter on stack + MOV AL,BPB_MCB ;media control byte + STOSB ;store media control byte, increment DI + MOV AX,0FFFFH ;bytes 2 and 3 of FAT are 0FFH + STOSW + MOV AX,BPB_FATSZ ;number of sectors per FAT + MUL BPB_SSZ ;* sector size = length of FAT in bytes + SUB AX,3 ;less the 3 bytes we've stored + MOV CX,AX ;count to CX + XOR AX,AX + REP STOSB ;clear remainder of FAT + POP CX ;get loop counter off stack + LOOP FORMAT_VDISK_A ;loop for all copies of the FAT + +;Format the directory + + MOV SI,OFFSET VOL_LABEL ;point to volume label + MOV CX,VOL_LABEL_LEN ;length of volume directory entry + REP MOVSB ;move volume id to 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 ;CX = length of rest of directory + XOR AX,AX + REP STOSB ;clear directory to nulls + RET +FORMAT_VDISK ENDP + + SUBTTL MOVE_VDISK + PAGE +;-----------------------------------------------------------------------; +; MOVE_VDISK moves the formatted boot sector, FAT, and directory ; +; into extended memory. ; +;-----------------------------------------------------------------------; + +MOVE_VDISK PROC + MOV AL,BPB_FATN ;number of FAT copies + CBW ;clear AH + MUL BPB_FATSZ ;number of FAT sectors + ADD AX,BPB_RES ;+ reserved sectors + ADD AX,DIR_SECTORS ;+ directory sectors + MOV MOVE_RHCNT,AX ;store as I/O length + + MOV BX,OFFSET MOVE_RH ;DS:BX point to request header + PUSH DS ;make sure DS gets preserved + CALL INOUT ;move to extended memory + POP DS + RET +MOVE_VDISK ENDP + + SUBTTL UPDATE_BOOT + PAGE +;-----------------------------------------------------------------------; +; UPDATE_BOOT updates the BOOT_EM word in the first extended ; +; memory VDISK (address 10 001EH) to show the kilobyte address ; +; of the first extended memory byte not used by any VDISK buffer. ; +;-----------------------------------------------------------------------; +UPDATE_BOOT PROC + PUSH DS + MOV DS,FIRST_VDISK ;set DS to first VDISK + MOV AX,DS:AVAIL_LO ;24-bit end address of all VDISKs + MOV DL,DS:AVAIL_HI + XOR DH,DH + POP DS + DIV C1024 ;address / 1024 + MOV BOOT_EM,AX ;store in temporary location + + MOV AX,2 ;length of block move is 2 bytes + MOV TGT.DESC_LMT,AX + MOV SRC.DESC_LMT,AX + + MOV AX,PARA_SIZE ;16 + MOV CX,CS ;our segment address + MUL CX ;16 * segment address + ADD AX,OFFSET BOOT_EM ;+ offset of source data + ADC DL,0 ;pick up any carry + + MOV SRC.DESC_BASEL,AX ;store source base address + MOV SRC.DESC_BASEH,DL + + MOV TGT.DESC_BASEL,BOOT_EM_OFF ;offset of BOOT_EM + MOV TGT.DESC_BASEH,10H ;1 megabyte + + MOV CX,1 ;move 1 word + + PUSH CS + POP ES + MOV SI,OFFSET GDT ;ES:DI point to global descriptor table + + MOV AH,EM_BLKMOVE ;function code + INT EM_INT ;move BOOT_EM to 10 001EH + RET +UPDATE_BOOT ENDP + + SUBTTL STEAL_INT19 + PAGE +;-----------------------------------------------------------------------; +; STEAL_INT19 changes the INT 19H vector to point to this VDISK ; +; so that subsequent extended memory VDISKS may locate the ; +; AVAIL_HI and AVAIL_LO fields to determine their buffer start ; +; addresses. ; +;-----------------------------------------------------------------------; +STEAL_INT19 PROC + PUSH DS + XOR AX,AX + MOV DS,AX ;set DS = 0 + ASSUME DS:INT_VEC + CLI ;disable interrupts + LES DI,DS:BOOT_VEC ;get original vector's content + MOV CS:INTV19O,DI ;save original vector + MOV CS:INTV19S,ES + MOV DS:BOOT_VECO,OFFSET VDISK_INT19 ;offset of new INT routine + MOV DS:BOOT_VECS,CS ;segment of new INT routine + STI ;enable interrupts again + POP DS ;restore DS + RET +STEAL_INT19 ENDP + + SUBTTL FILL_RH Fill in Request Header + PAGE +;-----------------------------------------------------------------------; +; FILL_RH fills in the Request Header returned to DOS ; +;-----------------------------------------------------------------------; + ASSUME DS:CSEG +FILL_RH PROC ;fill in INIT Request Header fields + MOV CX,START_BUFFER_PARA ;segment end of VDISK resident code + MOV AX,PARAS_PER_SECTOR ;paragraphs per sector + MUL BPB_SECN ;* number of sectors + ADD AX,CX ;+ starting segment + MOV DX,AX ;DX is segment of end VDISK buffer + CMP EM_SW,0 ;if extended memory not requested + JE FILL_RH_A ;skip DX adjustment + + MOV DX,CX ;end of code segment addr +FILL_RH_A: ;DX is proper ending segment address + MOV AL,1 ;number of units + test CS:err_flag2,err_baddos + jnz dont_install + + TEST ERR_FLAG,ERR_SYSSZ+ERR_EXTSW ;if bypassing install + JZ FILL_RH_B ;jump if installing driver + +dont_install: + MOV DX,CS ;segment of end address + XOR AL,AL ;number of units is zero +FILL_RH_B: + PUSH DS ;preserve DS + LDS BX,RH_PTRA ;get Request Header addr in DS:BX + MOV RH.RH0_NUN,AL ;store number of units (0 or 1) + MOV RH.RH0_ENDO,0 ;end offset is always zero + MOV RH.RH0_ENDS,DX ;end of VDISK or end of buffer + MOV RH.RH0_BPBO,OFFSET BPB_PTR + MOV RH.RH0_BPBS,CS ;BPB array address + POP DS ;restore DS + RET +FILL_RH ENDP + + SUBTTL WRITE_MESSAGES and associated routines + PAGE +;-----------------------------------------------------------------------; +; WRITE_MESSAGE writes a series of messages to the standard ; +; output device showing the VDISK parameter values actually used. ; +;-----------------------------------------------------------------------; + +CHAR4 DB 'nnnn$' ;build 4 ASCII decimal digits + + ASSUME DS:CSEG +WRITE_MESSAGES PROC ;display all messages + + MSG IMSG ;'VDISK virtual disk $' + + test CS:err_flag2,err_baddos + jz check_dos_version + + msg errm8 + ret + +;If DOS Version 3.x is in use, the Request Header contains a drive code +;that is displayed to show which drive letter was assigned to this +;VDISK. This field is not present in the DOS Version 2 Request Header. + +check_dos_version: + MOV AH,DOS_VERS ;get DOS version call + INT DOS ;invoke DOS + + CMP AL,3 ;DOS Version 3 or greater? + JB WRITE_MESSAGES_A ;no, bypass drive letter + + PUSH DS ;preserve DS + LDS BX,RH_PTRA ;get Request Header Address + MOV DL,RH.RH0_DRIV ;get drive code + ADD DL,'A' ;convert to drive letter + POP DS ;restore DS + + MOV AH,DOS_PCHR ;function code to write character in DL + INT DOS ;display drive letter + + MOV DL,':' ;display trailing colon + INT DOS + +WRITE_MESSAGES_A: + MSG MSGCRLF ;end the first line + +;If any of the user specified values has been adjusted, issue an +;appropriate message + + TEST ERR_FLAG,ERR_BSIZE ;was buffersize adjusted? + JZ WRITE_MESSAGES_B ;if not, skip message + + MSG ERRM1 ;buffer size adjusted + +WRITE_MESSAGES_B: + TEST ERR_FLAG,ERR_SSZ ;was sector size adjusted? + JZ WRITE_MESSAGES_C ;if not, skip message + + MSG ERRM2 ;sector size adjusted + +WRITE_MESSAGES_C: + TEST ERR_FLAG,ERR_DIRN ;were directory entries adjusted? + JZ WRITE_MESSAGES_D0 ;if not, skip message + + MSG ERRM3 ;directory entries adjusted + +WRITE_MESSAGES_D0: + TEST ERR_FLAG,ERR_ESIZE ;was transfer size adjusted? + JZ WRITE_MESSAGES_D ;if not, skip message + + MSG ERRM7 ;transfer size adjusted + +WRITE_MESSAGES_D: + TEST ERR_FLAG,ERR_SWTCH ;was an invalid switch character found? + JZ WRITE_MESSAGES_E ;if not, skip message + + MSG ERRM5 ;invalid switch character + +WRITE_MESSAGES_E: + TEST ERR_FLAG,ERR_SYSSZ ;is system size too small to install? + JZ WRITE_MESSAGES_F ;if not, bypass error message + + MSG ERRM4 ;too large for system storage + RET ;skip messages showing adjusted sizes + +WRITE_MESSAGES_F: + TEST ERR_FLAG,ERR_EXTSW ;extender card switches wrong? + JZ WRITE_MESSAGES_G ;if not, bypass error message + + MSG ERRM6 ;extender card switches wrong msg + RET ;skip remaining messages + +WRITE_MESSAGES_G: ;display adjusted size messages + MSG MSG1 ;buffer size: + + MOV DX,BUFF_SIZE ;buffer size in binary + CALL STOR_SIZE ;convert binary to ASCII decimal + MSG CHAR4 ;print 4 decimals + MSG MSG2 ;KB,CR,LF + + MSG MSG3 ;sector size: + MOV DX,BPB_SSZ + CALL STOR_SIZE ;convert binary to ASCII decimal + MSG CHAR4 ;print 4 decimals + MSG MSGCRLF ;finish off line + + MSG MSG4 ;directory entries: + MOV DX,BPB_DIRN ;number of directory entries + CALL STOR_SIZE + MSG CHAR4 ;print 4 decimals + MSG MSGCRLF ;finish off the line + + CMP CS:EM_SW,0 ;extended memory ? + JE END_LINE ; + MSG MSG5 ;transfer size: + MOV DX,MAXSEC_TRF + CALL STOR_SIZE ;convert binary to ASCII decimal + MSG CHAR4 ;print 4 decimals + MSG MSGCRLF ;finish off line + +END_LINE: + MSG MSGCRLF ;one more blank line to set it off + RET ;return to INIT_P1 + +;SHOW_MSG displays a string at DS:DX on the standard output device +;String is terminated by a $ + +SHOW_MSG PROC ;display string at DS:DX + PUSH AX ;preserve AX across call + MOV AH,DOS_PSTR ;DOS function code + INT DOS ;invoke DOS print string function + POP AX ;restore AX + RET +SHOW_MSG ENDP + +;STOR_SIZE converts the content of DX to 4 decimal characters in CHAR4 +;(DX must be <= 9999) + +STOR_SIZE PROC ;convert DX to 4 decimals in CHAR4 + ;develop 4 packed decimal digits in AX + XOR AX,AX ;clear result register + MOV CX,16 ;shift count +STOR_SIZE_B: + SHL DX,1 ;shift high bit into carry + ADC AL,AL ;double AL, carry in + DAA ;adjust for packed decimal + XCHG AL,AH + ADC AL,AL ;double high byte, carry in + DAA + XCHG AL,AH + LOOP STOR_SIZE_B ;AX contains 4 packed decimal digits + + PUSH CS + POP ES ;point ES:DI to output string + MOV DI,OFFSET CHAR4 + + MOV CX,1310H ;10H in CL is difference between blank and zero + ;13H in CH is decremented and ANDed to force + ;last character not to be zero suppressed + PUSH AX ;save AX on stack + MOV DL,AH ;2 decimals to DL + CALL STOR_SIZE_2 ;display DL as 2 decimal characters + POP DX ;bring low 2 decimals into DL +STOR_SIZE_2: ;display DL as 2 decimal characters + MOV DH,DL ;save 2 decimals in DH + SHR DL,1 ;shift high order decimal right to low position + SHR DL,1 + SHR DL,1 + SHR DL,1 + CALL STOR_SIZE_1 ;display low nibble of DL + MOV DL,DH ;get low decimal from pair +STOR_SIZE_1: ;display low nibble of DL as 1 decimal char + AND DL,0FH ;clear high nibble + JZ STOR_SIZE_Z ;if digit is significant, + XOR CL,CL ;defeat zero suppression +STOR_SIZE_Z: + DEC CH ;decrement zero suppress counter + AND CL,CH ;always display least significant digit + OR DL,'0' ;convert packed decimal to ASCII + SUB DL,CL ;zero suppress (nop or change '0' to ' ') + MOV AL,DL ;char to DL + STOSB ;store char at ES:DI, increment DI + RET +STOR_SIZE ENDP + +WRITE_MESSAGES ENDP + +INIT_P1 ENDP ;end of INIT part one + +;-----------------------------------------------------------------------; +; VDISK Message definitions ; +;-----------------------------------------------------------------------; + +IMSG DB 'VDISK virtual disk ','$' + +ERRM1 DB ' Buffer size adjusted',CR,LF,'$' +ERRM2 DB ' Sector size adjusted',CR,LF,'$' +ERRM3 DB ' Directory entries adjusted',CR,LF,'$' +ERRM4 DB ' VDISK not installed - insufficient memory' + DB CR,LF,CR,LF,BEL,'$' +ERRM5 DB ' Invalid switch character',CR,LF,'$' +ERRM6 DB ' VDISK not installed - Extender Card switches' + DB CR,LF + DB ' do not match system memory size' + DB CR,LF,CR,LF,BEL,'$' +ERRM7 DB ' Transfer size adjusted',CR,LF,'$' +ERRM8 DB ' VDISK not installed - Incorrect DOS version' + DB CR,LF,CR,LF,BEL,'$' + +MSG1 DB ' Buffer size: $' +MSG2 DB ' KB' +MSGCRLF DB CR,LF,'$' +MSG3 DB ' Sector size: $' +MSG4 DB ' Directory entries: $' +MSG5 DB ' Transfer size: $' +MSGEND LABEL BYTE ; End of message text + +CSEG ENDS + END diff --git a/v4.0/src/DEV/VDISK/VDISK.INC b/v4.0/src/DEV/VDISK/VDISK.INC new file mode 100644 index 0000000..202a008 --- /dev/null +++ b/v4.0/src/DEV/VDISK/VDISK.INC @@ -0,0 +1,28 @@ +EMS_STATUS EQU 40H +EMS_GET_NUM_PAGES EQU 42H +EMS_ALLOC_PAGES EQU 43H +EMS_MAP_HANDLE EQU 44H +EMS_VERSION EQU 46H +EMS_SAVE_STATE EQU 47H +EMS_RESTORE_STATE EQU 48H +EMS_GET_FRAME_ADDR EQU 5100H +EMS_INT EQU 67H + +EMS_EXPECTED_VERSION EQU 34H +EMS_LOW_ERROR EQU 80H +EMS_INSTALLED_FLAG EQU -1 + +EMS_ALLOC_ERROR EQU -1 ; ***RPS ?? +SINGLE_SEGMENT EQU 1 +DOS_PAGE1 EQU 0FEH +DOS_PAGE2 EQU 0FFH +DOS_PAGE_SZ EQU 16 + +GET_PAGE_FRAME_STRUC STRUC + START_PAGE DB ? + NUM_PAGE DB ? + FRAME_SEGMENT DW ? +GET_PAGE_FRAME_STRUC ENDS +BUFFER_ENTRY_SIZE EQU TYPE GET_PAGE_FRAME_STRUC + + \ No newline at end of file diff --git a/v4.0/src/DEV/VDISK/VDISK.LNK b/v4.0/src/DEV/VDISK/VDISK.LNK new file mode 100644 index 0000000..74989ed --- /dev/null +++ b/v4.0/src/DEV/VDISK/VDISK.LNK @@ -0,0 +1,3 @@ +VDISKSYS.OBJ +VDISK.EXE; + \ No newline at end of file diff --git a/v4.0/src/DEV/VDISK/VDISK.SKL b/v4.0/src/DEV/VDISK/VDISK.SKL new file mode 100644 index 0000000..9555298 --- /dev/null +++ b/v4.0/src/DEV/VDISK/VDISK.SKL @@ -0,0 +1,36 @@ +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; VDISK Message Skeleton file +; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +:util VDISK ;AN000; + +:class 2 ;AN000; +:use PARSE1 ;AN000; +:use PARSE2 ;AN000; +:use PARSE3 ;AN000; +:use PARSE4 ;AN000; +:use PARSE6 ;AN000; +:use PARSE7 ;AN000; +:use PARSE8 ;AN000; + +:class A ;AN000; +:use 1 COMMON1 ;AN000; "Incorrect DOS version" +:def 2 "Insufficient memory",CR,LF ;AN000; "Insufficient memory" + +:def 3 "VDISK Version 4.00 virtual disk %1",CR,LF ;AN000; +:def 4 " Buffer size adjusted",CR,LF ;AN000; +:def 5 " Sector size adjusted",CR,LF ;AN000; +:def 6 " Directory entries adjusted",CR,LF ;AN000; +:def 7 " Invalid switch character",CR,LF ;AN000; +:def 8 " Transfer size adjusted",CR,LF ;AN000; +:def 9 " Buffer size: %1 KB",CR,LF ;AN000; +:def 10 " Sector size: %1",CR,LF ;AN000; +:def 11 " Directory entries: %1",CR,LF ;AN000; +:def 12 " Transfer size: %1",CR,LF ;AN000; +:def 13 "VDISK not installed - " ;AN000; +:def 14 " Extender Card switches",CR,LF ;AN000; + "do not match system memory size",CR,LF,CR,LF ;AN000; + +:end ;AN000; + + diff --git a/v4.0/src/DEV/VDISK/VDISKMSG.ASM b/v4.0/src/DEV/VDISK/VDISKMSG.ASM new file mode 100644 index 0000000..4b43a31 --- /dev/null +++ b/v4.0/src/DEV/VDISK/VDISKMSG.ASM @@ -0,0 +1,27 @@ + PAGE ,132 + TITLE VDISKMSG - VDISK message library + +;All messages issued by VDISK are defined here for ease in +;translation for international use. + +;Each message should end with a '$' to delimit the end of the string. + +CSEG SEGMENT PARA PUBLIC 'CODE' + + PUBLIC IMSG + PUBLIC ERRM1,ERRM2,ERRM3,ERRM4,ERRM5,ERRM6,ERRM7,errm8 + PUBLIC MSG1,MSG2,MSG3,MSG4,MSG5,MSGCRLF + PUBLIC MSGEND + +;ASCII equates + +BEL EQU 07H ;alarm +LF EQU 0AH ;line feed +CR EQU 0DH ;carriage return + + +include vdiskmsg.inc + +MSGEND LABEL BYTE ;must be last in module +CSEG ENDS + END diff --git a/v4.0/src/DEV/VDISK/VDISKSYS.ASM b/v4.0/src/DEV/VDISK/VDISKSYS.ASM new file mode 100644 index 0000000..544c610 --- /dev/null +++ b/v4.0/src/DEV/VDISK/VDISKSYS.ASM @@ -0,0 +1,3024 @@ + PAGE ,132 + TITLE VDISK - Virtual Disk Device Driver, Version 4.00 + +;VDISK simulates a disk drive, using Random Access Memory as the storage medium. + +;(C) Copyright Microsoft Corporation, 1984 - 1988 +;Licensed Material - Program Property of Microsoft Corp. + +;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) +; +;========================================================================= +; Change List +; +; AN000 - ver 4.0 specified changes +; AN001 - DCR 377 Modify VDISK Extended Memory allocation technique +; The allocation technique have been modified to +; allocate EM from the top down. To notify other +; users that EM has been used by VDISK, VDISK +; hooks function 88h, INT 15h. +; +; AN002 - PTM3214 EMS VDISK needed to be modified for two errors. +; AC002 The first related to VDISK returning the +; "Insufficient Memory" message when in fact +; there was enough EMS memory to support the +; requested VDISK. +; The second related to an EMS VDISK hanging when +; a program was invoked from an EMS VDISK with a +; non-standard sector size, i.e.; 128 bytes/sector, +; etc. This error was caused by the incorrect +; calculation of sectors per EMS page. +; +; AN003 - PTM3276 EMS VDISK causes a "Divide Overflow" message. +; AC003 This is caused by a byte divide that should +; be performed as a word divide. +; +; AN004 - PTM3301 EMS VDISK does not properly adjust the buffer +; size when too much EMS memory is requested. +; The code in UPDATE_AVAIL pertaining to EMS +; space allocation has been modified. +; +; AN005 - DCR474 Convert VDISK to support /E for extended memory +; and /X for expanded memory. +; +; AN006 - PTM4729 Enable VDISK for the INT 2F call to determine +; the reserved EMS page for VDISK and FASTOPEN +; +;========================================================================= + +;Message text for VDISK is in module VDISKMSG. +.xlist + INCLUDE VDISKSYS.INC + INCLUDE SYSMSG.INC + MSG_UTILNAME +.list + 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 +;;;;; DW ? ;reserved for BIOS message flag ;an006; dms; +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_FLAG DW 0 ;initialized to no error ;an000; dms; +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 8800H ;memory size determination in KB + +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 + +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 + + SUBTTL Resident Data Area + PAGE + +;-----------------------------------------------------------------------; +; Map INT 15H vector in low storage ; +;-----------------------------------------------------------------------; +INT_VEC SEGMENT AT 00H + ORG 4*EM_INT +EM_VEC LABEL DWORD +EM_VECO DW ? ;offset +EM_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 V4.0' ;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 + +INTV15 LABEL DWORD +INTV15O DW ? ;offset +INTV15S 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 + +EM_New_Size dw ? ;an001; dms;new size for EM +EM_KSize dw ? ;an001; dms;size of EM currently. + +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 + +;-----------------------------------------------------------------------; +; EMS Support ; +;-----------------------------------------------------------------------; + +EM_SW2 DB not EMS_Installed_Flag ;ac006;Default if EMS not installed +EMS_HANDLE DW ? ;AN000; EMS handle for reference +EMS_FRAME_ADDR DW ? ;AN000; EMS handle for reference +EMS_CURR_SECT DW ? ;an000; Current EMS sector being addressed +CURR_EMS_PAGE DW ? ;ac002; Current EMS page number +SECT_LEFT_IN_FRAME DW ? ;AN000; Sectors left to transfer in this frame +SECT_PER_PAGE DW ? ;AN000; Sectors per page +DOS_Page dw ? ;an006; EMS physical page for VDISK + +EMS_SAVE_ARRAY DB 80h dup(0) ;an000; save current state of ems +EMS_SEG_ARRAY DD ? ;an000; save segment array + +CURR_DTA_OFF DW ? ;AN000; DMS;CURRENT OFFSET OF DTA + +PC_386 DB false ;AN000; DMS;386 machine flag + +SUBLIST STRUC ;AN000;SUBLIST STRUCTURE + +SL_SIZE DB ? ;AN000;SUBLIST SIZE +SL_RES DB ? ;AN000;RESERVED +SL_OFFSET DW ? ;AN000;PARM OFFSET +SL_SEGMENT DW ? ;AN000;PARM SEGMENT +SL_ID DB ? ;AN000;NUMBER OF PARM +SL_FLAG DB ? ;AN000;DISPLAY TYPE +SL_MAXW DB ? ;AN000;MAXIMUM FIELD WIDTH +SL_MINW DB ? ;AN000;MINIMUM FIELD WIDTH +SL_PAD DB ? ;AN000;PAD CHARACTER + +SUBLIST ENDS ;AN000;END SUBLIST STRUCTURE + + +BIOS_SYSTEM_DESCRIPTOR struc ;AN000;SYSTEM TYPE STRUC + +bios_SD_leng dw ? ;AN000;VECTOR LENGTH +bios_SD_modelbyte db ? ;AN000;SYSTEM MODEL TYPE +bios_SD_scnd_modelbyte db ? ;AN000; + db ? ;AN000; +bios_SD_featurebyte1 db ? ;AN000; + db 4 dup (?) ;AN000; + +BIOS_SYSTEM_DESCRIPTOR ends ;AN000;END OF STRUC + +;-----------------------------------------------------------------------; +; 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 15H (size) interrupt handler + PAGE +;-----------------------------------------------------------------------; +; INT 15H Interrupt Handler routine ; +;-----------------------------------------------------------------------; + +;========================================================================= +; VDISK_INT15 : This routine traps the INT 15h requests to perform its +; own unique services. This routine provides 1 INT 15h +; service; function 8800h. +; +; Service - Function 8800h: Obtains the size of EM from the word +; value EM_KSize +; Call With: AX - 8800h +; Returns : AX - Kbyte size of EM +; +;========================================================================= +VDISK_INT15 PROC ;an001; dms; + + cmp ah,EM_Size_Get ;an001; dms;function 88h +; $if e ;an001; dms;get size + JNE $$IF1 + mov ax,cs:EM_KSize ;an001; dms;return size + clc ;an001; dms;clear CY +; $else ;an001; dms; + JMP SHORT $$EN1 +$$IF1: + jmp cs:INTV15 ;an001; dms;jump to org. vector +; $endif ;an001; dms; +$$EN1: + + iret ;an001; dms; + +VDISK_INT15 ENDP ;an001; dms; + + + 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: +;;;;; PUSH DS ;ICE +;;;;; push bx ;ICE +;;;;; push ax ;ICE + +;;;;; mov bx,0140H ;ICE +;;;;; xor ax,ax ;ICE +;;;;; mov ds,ax ;ICE +;;;;; mov ax,word ptr ds:[bx] ;ICE +;;;;; mov word ptr ds:[bx],ax ;ICE + +;;;;; pop ax ;ICE +;;;;; pop bx ;ICE +;;;;; POP DS ;ICE + +;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 + + MOV CS:SECT_LEFT_IN_FRAME,AX ;AN000; 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: + + CALL INOUT_D_LOW_MEM ;AN000;LOW MEMORY TRANSFER + +;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 + + + CMP EM_SW,EM_Mem ;AC005; Is EM requested? + JE INOUT_EM_A ;AN000; + JMP INOUT_EMS ;AN000; Yes, compute page + +INOUT_EM_A: ;AN000; No, compute 24-bit address + +;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 + + JZ INOUT_UPDATE ; + JMP INOUT_EM_XE ;jump if I/O error encountered + +INOUT_UPDATE: + +;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 + + JMP INOUT_EM_X2 ;AN000; All done . . . exit +;-----------------------------------------------------------------------; +; EMS Support ;RPS; ; +;-----------------------------------------------------------------------; + +INOUT_EMS: ;AN000; + +; Save EMS state in case anyone is using it + PUSH AX ;AN000; DMS;SAVE IT + PUSH BX ;AN000; DMS;SAVE IT + PUSH DX ;AN000; DMS;SAVE IT + push di ;an000; dms;save it + push si ;an000; dms;save it + push ds ;an000; dms;save it + push es ;an000; dms;save it + mov ax,cs ;an000; dms;transfer cs to ds/es + mov ds,ax ;an000; dms; + mov es,ax ;an000; dms; + + mov di,offset cs:EMS_SAVE_ARRAY ;an000; point to save area + mov si,offset cs:EMS_SEG_ARRAY ;an000; point to segment area + mov word ptr cs:EMS_SEG_ARRAY,0001h ;an000; 1 segment to save + mov ax,cs:EMS_Frame_Addr ;an000; get segment + mov word ptr cs:EMS_SEG_ARRAY+2,ax ;an000; segment + MOV AX,EMS_SAVE_STATE ;AN000; Function code to save active handle state + INT EMS_INT ;AN000; + + pop es ;an000; dms;restore + pop ds ;an000; dms;restore + pop si ;an000; dms;restore + pop di ;an000; dms;restore + POP DX ;AN000; DMS;RESTORE + POP BX ;AN000; DMS;RESTORE + POP AX ;AN000; DMS;RESTORE + +; Compute offset and segment of VDISK frame for starting segment in CX:SI +; and page containing VDISK starting sector + + push ds ;an000; dms;save ds + push es ;an000; dms;save es + + mov cs:curr_dta_off,0 ;an000; dms;current offset = 0 + mov ax,rh.rh4_ssn ;an000; dms;get 1st. sector + mov cs:ems_curr_sect,ax ;an000; dms;save it + call ems_page_off_calc ;an000; dms;calc page and off. + call ems_dta_calc ;an000; dms;calc DTA + call ems_src_tgt ;an000; dms;get src & tgt + +; $do ;an000; dms;while sectors left +$$DO4: + call map_frame ;an000; dms;map a page + call ems_trf ;an000; dms;transfer data + dec cs:sect_left ;an000; dms;sect_left - 1 + cmp cs:sect_left,0 ;an000; dms;continue? +; $leave e ;an000; dms;no - exit + JE $$EN4 + ; yes - continue + mov ax,cs:ems_curr_sect ;an000; dms;get current sector + call ems_page_off_calc ;an000; dms;calc page and off. + call ems_dta_adj ;an000; dms;adjust DTA + call ems_src_tgt ;an000; dms;get src & tgt +; $enddo ;an000; dms;end while + JMP SHORT $$DO4 +$$EN4: + + pop es ;an000; dms;restore es + pop ds ;an000; dms;restore ds + +; Restore EMS state in case anyone was using it + ;AN000; No, + PUSH AX ;AN000; DMS;SAVE IT + PUSH BX ;AN000; DMS;SAVE IT + PUSH DX ;AN000; DMS;SAVE IT + PUSH SI ;AN000; DMS;SAVE IT + push ds ;an000; dms;save it + + mov ax,cs ;an000; dms;get cs + mov ds,ax ;an000; dms;put in ds + MOV AX,EMS_RESTORE_STATE ;AN000; Function code to restore active handle state + MOV SI,OFFSET CS:EMS_SAVE_ARRAY ;AN000; POINT TO SAVE ARRAY + INT EMS_INT ;AN000; + + pop ds ;an000; dms;restore + POP SI ;AN000; DMS;RESTORE + POP DX ;AN000; DMS;RESTORE + POP BX ;AN000; DMS;RESTORE + POP AX ;AN000; DMS;RESTORE + +;-----------------------------------------------------------------------; + +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 + + +;========================================================================= +; EMS_PAGE_OFF_CALC : Calculates the current ems page to use and +; the offset of the requested sector in that +; page. +; +; Inputs: AX - Sector for input/output +; SECT_PER_PAGE - # of sectors/ems page +; BPB_SSZ - Size in bytes of a sector +; EMS_FRAME_ADDR - Segment of ems page +; +; Outputs: CURR_EMS_PAGE - Currently active ems page +; CX:SI - Segment:Offset of logical sector +;========================================================================= + +ems_page_off_calc proc near ;an000; dms;calc page/offset + + xor dx,dx ;an002; dms;clear high word + div cs:sect_per_page ;an000; dms;determine page + mov cs:curr_ems_page,ax ;an002; dms;save page + mov ax,dx ;an002; dms;offset calc + mul cs:bpb_ssz ;an000; dms;calc offset + mov si,ax ;an000; dms;save sector offset + mov cx,cs:ems_frame_addr ;an000; dms;obtain sector seg + + ret ;an000; dms; + +ems_page_off_calc endp ;an000; dms; + + +;========================================================================= +; EMS_DTA_CALC : Calculate the DTA buffer to be used. +; +; Inputs: PARA_SIZE - 16 +; RH4_DTAS - Segment of DTA from request packet +; RH4_DTA0 - Offset of DTA from request packet +; WPARA_SIZE- 16 +; +; Outputs: AX:DX - Segment:Offset of DTA buffer +;========================================================================= + +ems_dta_calc proc near ;an000; dms;calc DTA buffer + + xor dx,dx ;an002; dms;clear high word + mov ax,para_size ;an000; dms;get para size + mul rh.rh4_dtas ;an000; dms;times DTA segment + add ax,rh.rh4_dtao ;an000; dms;+ DTA offset + adc dx,0 ;an002; dms;pick up carry + div cs:wpara_size ;an000; dms;/16 + + ret ;an000; dms; + +ems_dta_calc endp ;an000; dms; + + +;========================================================================= +; EMS_DTA_ADJ : Adjust DTA for the number of sectors having +; been transferred. +; +; External Calls : EMS_DTA_CALC +; +; Inputs: CURR_DTA_OFF - Current offset value to be adjusted. +; +; Outputs: CURR_DTA_OFF - Adjusted offset value into DTA +; DX - Adjusted offset value into DTA +;========================================================================= + +ems_dta_adj proc near ;an000; dms;adjust DTA + + call ems_dta_calc ;an000; dms; + push ax ;an000; dms;save reg + mov ax,cs:curr_dta_off ;an000; dms;get current off. + add ax,cs:bpb_ssz ;an000; dms;adjust up + mov cs:curr_dta_off,ax ;an000; dms;save new off + add dx,ax ;an000; dms;set dx to new off + pop ax ;an000; dms;restore reg + ret ;an000; dms; + +ems_dta_adj endp ;an000; dms; + +;========================================================================= +; EMS_SRC_TGT : Determine the source and target segments for +; data transfer. +; +; Inputs: RHC_CMD - Request packet command identifier +; AX:DX - DTA +; CX:SI - EMS page/sector +; +; Outputs: IO_SRCS - Segment of source of trf +; IO_SRCO - Offset of source of trf +; IO_TGTS - Segment of target for trf +; IO_TGTO - Offset of target for trf +;========================================================================= + +ems_src_tgt proc near ;an000; dms;src/tgt calc + + cmp rh.rhc_cmd,cmd_input ;an000; dms;input/output? +; $if ne ;an000; dms; + JE $$IF7 + xchg ax,cx ;an000; dms;swap src/tgt seg + xchg dx,si ;an000; dms;swap src/tgt off +; $endif ;an000; dms; +$$IF7: + + mov cs:io_srcs,cx ;an000; dms;save src seg + mov cs:io_srco,si ;an000; dms;save src off + mov cs:io_tgts,ax ;an000; dms;save tgt seg + mov cs:io_tgto,dx ;an000; dms;save tgt off + ret ;an000; dms; + +ems_src_tgt endp ;an000; dms; + + +;========================================================================= +; EMS_TRF : Perform the sector transfer of data. +; +; Inputs: BPB_SSZ - Sector size +; IO_SRCA - Source address +; IO_TGTA - Target address +; +; Outputs: Transferred data +; EMS_CURR_SECT - Incremented 1 +;========================================================================= + +ems_trf proc near ;an000; dms;transfer data + + mov ax,cs:bpb_ssz ;an000; dms;set to sector size + shr ax,1 ;an000; dms;make words + mov cx,ax ;an000; dms;set loop counter + push ds ;an000; dms;save regs + push es ;an000; dms; + + lds si,cs:io_srca ;an000; dms;get src address + les di,cs:io_tgta ;an000; dms;get tgt address + + CMP CS:PC_386,TRUE ;AN000; DO WE HAVE A 386 MACHINE? +; $IF E ;AN000; YES + JNE $$IF9 + SHR CX,1 ;AN000; /2 = DW COUNT + DB 66H ;AN000; SIMULATE A MOVSDW +; $ENDIF ;AN000; +$$IF9: + rep movsw ;an000; dms;perform transfer + + pop es ;an000; dms;restore regs + pop ds ;an000; dms; + inc cs:ems_curr_sect ;an000; dms;increment sector + + ret ;an000; dms; + +ems_trf endp ;an000; dms; + + + +MAP_FRAME PROC NEAR ;AN000; + + PUSH BX ;AN000; DMS; + mov ax,cs:DOS_Page ;an000; get physical page + MOV AH,EMS_MAP_HANDLE ;AN000; EMS function to map page + MOV BX,CS:CURR_EMS_PAGE ;AN000; Page number + MOV DX,CS:EMS_HANDLE ;AN000; EMS handle + INT EMS_INT ;AN000; + POP BX ;AN000; DMS; + RET ;AN000; + +MAP_FRAME ENDP ;AN000; + + +INOUT_D_LOW_MEM PROC NEAR ;AN000; LOW MEMORY TRANSFER + + 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 + + CMP CS:PC_386,TRUE ;AN000; DO WE HAVE A 386 MACHINE? +; $IF E ;AN000; YES + JNE $$IF11 + SHR CX,1 ;AN000; /2 = DW COUNT + DB 66H ;AN000; SIMULATE A MOVSDW +; $ENDIF ;AN000; +$$IF11: + REP MOVSW ;AN000; PERFORM DOUBLE WORD MOVE + +;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 + + RET + +INOUT_D_LOW_MEM 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 'VDISKx.x' ;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) + +;-----------------------------------------------------------------------; +; 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 ' ' + DB ' ' + DB ' ' + +MAXSEC_TRF DW 0 ;maximum number of sectors to transfer when + ;in extended memory + +BUFF_SIZE DW 0 ;desired VDISK buffer size in kilobytes + +MIN_MEMORY_LEFT DW 100 ;minimum amount of system memory (kilobytes) + ;that must remain after VDISK is installed + +PARA_PER_KB DW 1024/PARA_SIZE ;paragraphs in one kilobyte +C1024 DW 1024 ;bytes in one kilobyte +DIRE_SIZE DW DIR_ENTRY_SIZE ;bytes in one directory entry +DIR_SECTORS DW ? ;number of sectors of directory + +ERR_FLAG DB 0 ;error indicators to condition messages +ERR_BSIZE EQU 80H ;buffer size adjusted +ERR_SSZ EQU 40H ;sector size adjusted +ERR_DIRN EQU 20H ;number of directory entries adjusted +ERR_PASS EQU 10H ;some adjustment made that requires + ;recomputation of values previously computed +ERR_SSZB EQU ERR_SSZ+ERR_PASS ;sector size altered this pass +ERR_SYSSZ EQU 08H ;system storage too small for VDISK +ERR_SWTCH EQU 04H ;invalid switch character +ERR_EXTSW EQU 02H ;extender card switches don't match memory size +ERR_ESIZE EQU 01H ;Transfer size adjusted + +DOS_PG_SZ DB DOS_PAGE_SZ ;AN000; +DOS_Page_Size_Word dw DOS_Page_Sz ;an000; + +err_flag2 db 0 +err_baddos equ 01h ; Invalid DOS Version + +;-----------------------------------------------------------------------; +; SUBLIST definitions and EQUATES for Message Retreiver ; +;-----------------------------------------------------------------------; + ;AN000; Message Number +INCORRECT_DOS EQU 1 ;AN000; +SYS_TOO_SMALL EQU 2 ;AN000; +VDISK_TITLE EQU 3 ;AN000; +BUFFER_ADJUSTED EQU 4 ;AN000; +SECTOR_ADJUSTED EQU 5 ;AN000; +DIR_ADJUSTED EQU 6 ;AN000; +INVALID_SW_CHAR EQU 7 ;AN000; +TRANS_ADJUSTED EQU 8 ;AN000; +BUF_SZ EQU 9 ;AN000; +SEC_SZ EQU 10 ;AN000; +DIR_ENTRIES EQU 11 ;AN000; +TRANS_SZ EQU 12 ;AN000; +VDISK_NOT_INST EQU 13 ;AN000; +EXTEND_CARD_WRONG EQU 14 ;AN000; + +NO_REPLACE EQU 0 ;AN000; CX = 0 -> No replacement +ONE_REPLACE EQU 1 ;AN000; CX = 1 -> One replacement +UNLIMITED_MAX EQU 0 ;AN000; MAX field - unlimited +SMALLEST_MIN EQU 1 ;AN000; MIN field - 1 character long +PAD_BLANK EQU 20H ;AN000; PAD character is a blank + + +DRIVE_CODE DB ? ;AN000; + DB ":",NULL ;AN000; ASCIIZ string for drive code + +; For the message: "VDISK Version 4.00 virtual disk %1",CR,LF + +TITLE_SUBLIST LABEL BYTE + +TT_SIZE DB 11 ;AN000; SUBLIST size (PTR to next SUBLIST) +TT_RESV DB 0 ;AN000; RESERVED +TT_VALUEO DW DRIVE_CODE ;AN000; Offset to ASCIIZ string +TT_VALUES DW ? ;AN000; SEGMENT TO ASCIIZ STRING +TT_ID DB 1 ;AN000; n of %n +TT_FLAG DB Left_Align+Char_Field_ASCIIZ +TT_MAXW DB UNLIMITED_MAX ;AN000; Maximum field width +TT_MINW DB SMALLEST_MIN ;AN000; Minimum field width +TT_PAD DB PAD_BLANK ;AN000; Character for Pad field + +; For the message: "Buffer size: %1 KB",CR,LF + +BUF_SZ_SUBLIST LABEL BYTE + +B_SIZE DB 11 ;AN000; SUBLIST size (PTR to next SUBLIST) +B_RESV DB 0 ;AN000; RESERVED +B_VALUEO DW BUFF_SIZE ;AN000; Offset to binary number +B_VALUES DW ? ;AN000; SEGMENT TO BINARY NUMBER +B_ID DB 1 ;AN000; n of %n +B_FLAG DB Left_Align+Unsgn_Bin_Word +B_MAXW DB UNLIMITED_MAX ;AN000; Maximum field width +B_MINW DB SMALLEST_MIN ;AN000; Minimum field width +B_PAD DB PAD_BLANK ;AN000; Character for Pad field + +; For the message: "Sector size: %1",CR,LF + +SEC_SZ_SUBLIST LABEL BYTE + +S_SIZE DB 11 ;AN000; SUBLIST size (PTR to next SUBLIST) +S_RESV DB 0 ;AN000; RESERVED +S_VALUEO DW BPB_SSZ ;AN000; Offset to binary number +S_VALUES DW ? ;AN000; SEGMENT TO BINARY NUMBER +S_ID DB 1 ;AN000; n of %n +S_FLAG DB Left_Align+Unsgn_Bin_Word +S_MAXW DB UNLIMITED_MAX ;AN000; Maximum field width +S_MINW DB SMALLEST_MIN ;AN000; Minimum field width +S_PAD DB PAD_BLANK ;AN000; Character for Pad field + +; For the message: "Directory entries: %1",CR,LF + +DIR_ENT_SUBLIST LABEL BYTE + +D_SIZE DB 11 ;AN000; SUBLIST size (PTR to next SUBLIST) +D_RESV DB 0 ;AN000; RESERVED +D_VALUEO DW BPB_DIRN ;AN000; Offset to binary number +D_VALUES DW ? ;AN000; SEGMENT TO BINARY NUMBER +D_ID DB 1 ;AN000; n of %n +D_FLAG DB Left_Align+Unsgn_Bin_Word +D_MAXW DB UNLIMITED_MAX ;AN000; Maximum field width +D_MINW DB SMALLEST_MIN ;AN000; Minimum field width +D_PAD DB PAD_BLANK ;AN000; Character for Pad field + +; For the message: "Transfer size: %1",CR,LF + +TRANS_SZ_SUBLIST LABEL BYTE + +T_SIZE DB 11 ;AN000; SUBLIST size (PTR to next SUBLIST) +T_RESV DB 0 ;AN000; RESERVED +T_VALUEO DW MAXSEC_TRF ;AN000; Offset to binary number +T_VALUES DW ? ;AN000; SEGMENT TO BINARY NUMBER +T_ID DB 1 ;AN000; n of %n +T_FLAG DB Left_Align+Unsgn_Bin_Word +T_MAXW DB UNLIMITED_MAX ;AN000; Maximum field width +T_MINW DB SMALLEST_MIN ;AN000; Minimum field width +T_PAD DB PAD_BLANK ;AN000; Character for Pad field + +;-----------------------------------------------------------------------; +; EMS Support ; +;-----------------------------------------------------------------------; + +FRAME_BUFFER DB 10 DUP(0) ;AN000; +VDISK_Name db "VDISK " ;an000; dms; + +four DB 4 + + + SUBTTL Initialization, Part one + PAGE +;-----------------------------------------------------------------------; +; Command Code 0 - Initialization ; +; At entry, DS:BX point to request header, AX = 0 ; +;-----------------------------------------------------------------------; +;Initialization is divided into two parts. +;This part, executed first, is later overlaid by the VDISK buffer. + +INIT_P1 PROC ;first part of initialization + +;;;;; PUSH DS ;ICE +;;;;; push bx ;ICE +;;;;; push ax ;ICE + +;;;;; mov bx,0140H ;ICE +;;;;; xor ax,ax ;ICE +;;;;; mov ds,ax ;ICE +;;;;; mov ax,word ptr ds:[bx] ;ICE +;;;;; mov word ptr ds:[bx],ax ;ICE + +;;;;; pop ax ;ICE +;;;;; pop bx ;ICE +;;;;; POP DS ;ICE + + MOV DX,SS ;save stack segment register + MOV CX,SP ;save stack pointer register + CLI ;inhibit interrupts while changing SS:SP + MOV AX,CS ;move CS to SS through AX + MOV SS,AX + MOV SP,OFFSET MSGEND ;end of VDISKMSG + ADD SP,STACK_SIZE ;+ length of our stack + + STI ;allow interrupts + + PUSH DX ;save old SS register on new stack + PUSH CX ;SAVE OLD SP REGISTER ON NEW STACK + + + + PUSH DS ;AN000;SAVE REGS + PUSH ES ;AN000; + + PUSH CS ;AN000;TRANSFER TO DS + POP DS ;AN000; + PUSH CS ;AN000; + POP ES ;AN000; + + ASSUME DS:CSEG,ES:CSEG ;AN000; + + CALL SYSLOADMSG ;AN000; LOAD messages and do DOS version check + JNC VDISK_CONTINUE_1 ;AN000; Did we load OK? + or cs:err_flag2,err_baddos + + push ds ;an006; dms;save ds + push bx ;an006; dms;save bx + lds bx,cs:RH_Ptra ;an006; dms;point to request header + mov RH.RH0_Flag,-1 ;an006; dms;signal BIO and error occurred + pop bx ;an006; dms;restore bx + pop ds ;an006; dms;restore ds + + MOV AX,VDISK_NOT_INST ;AN000;VDISK UNABLE TO BE INSTALLED + MOV BX,NO_HANDLE ;AN000;NO DISPLAY HANDLE + CALL SYSDISPMSG ;AN000;DISPLAY THE MESSAGE + MOV AX,INCORRECT_DOS ;AN000;BAD DOS VERSION + MOV BX,NO_HANDLE ;AN000;NO DISPLAY HANDLE + CALL SYSDISPMSG ;AN000;DISPLAY THE MESSAGE + +VDISK_CONTINUE_1: ;AN000; + + POP ES ;AN000;RESTORE REGS + POP DS ;AN000; + ASSUME DS:NOTHING,ES:NOTHING ;AN000; + + CALL PC_386_CHK ;AN000;SEE IF WE HAVE A 386 PC + ; Yes, continue + CALL GET_PARMS ;get parameters from CONFIG.SYS line + + PUSH CS + POP DS ;set DS = CS + ASSUME DS:CSEG,ES:CSEG + + CALL APPLY_DEFAULTS ;supply any values not specified + CALL DETERMINE_START ;compute start address of VDISK buffer + CALL VALIDATE ;validate parameters + CALL COPY_BPB ;Copy BIOS Parameter Block to boot record + + CALL VERIFY_EXTENDER ;Verify that extender card switches are right + + TEST ERR_FLAG,ERR_EXTSW ;are switches wrong? + JNZ INIT_P1_A ;if so, exit with messages + + test cs:err_flag2,err_baddos + jnz init_p1_a + + CMP EM_SW,0 ;EXTENDED MEMORY REQUEST? + JE INIT_P1_A ;jump if not + + TEST ERR_FLAG,ERR_SYSSZ ;is system too small for VDISK? + JNZ INIT_P1_A ;if so, don't do extended memory init + + CALL UPDATE_AVAIL ;update AVAIL_HI and AVAIL_LO to reflect + ;addition of extended memory VDISK + CALL FORMAT_VDISK ;construct a boot record, FATs and + ;directory in storage immediately + ;following this device driver + CALL MOVE_VDISK ;move formatted boot record, FATs, + ;and directory to extended memory + +INIT_P1_A: + CALL FILL_RH ;fill in INIT request header + CALL WRITE_MESSAGES ;display all messages + POP CX ;get old SP from stack + POP DX ;get old SS from stack + CLI ;disable interrupts while changing SS:SP + MOV SS,DX ;restore stack segment register + MOV SP,CX ;restore stack pointer register + STI ;enable interrupts +;-----------------------------------------------------------------------; +; INIT_P2 must be short enough to fit into the boot sector ; +; (minimum size of boot sector is 128 bytes), so we set up ; +; as many pointers as we can to help keep INIT_P2 short. ; +; ; +; ES:DI = storage address of first FAT sector ; +; BL = media control byte ; +; CX = number of FAT copies ; +; DX = number of bytes in one FAT, less 3 ; +; SI = offset of VOL label field ; +;-----------------------------------------------------------------------; + MOV ES,START_BUFFER_PARA ;start paragraph of VDISK buffer + + MOV AX,BPB_RES ;number of reserved sectors + MUL BPB_SSZ ;* sector size + MOV DI,AX ;ES:DI point to FAT start + + MOV BL,BPB_MCB ;media control byte + + MOV CL,BPB_FATN ;number of FAT copies + XOR CH,CH + + MOV AX,BPB_FATSZ ;FAT size in sectors + MUL BPB_SSZ ;* sector size = total FAT bytes + + SUB AX,3 ;-3 (FEFFFF stored by code) + MOV DX,AX + + MOV SI,OFFSET VOL_LABEL ;point to VOL label directory entry + JMP INIT_P2 ;jump to second part of initialization + ;this is redundant if the VDISK is in + ;extended memory, but is executed anyway + + + + SUBTTL PC_386_CHK + PAGE +;========================================================================= +; PC_386_CHK : QUERIES THE BIOS TO DETERMINE WHAT TYPE OF +; MACHINE WE ARE ON. WE ARE LOOKING FOR A 386. +; THIS WILL BE USED TO DETERMINE IF A DW MOVE +; IS TO BE PERFORMED. +; +; INPUTS : NONE +; +; OUTPUTS : PC_386 - FLAG SIGNALS IF WE ARE ON A 386 MACHINE. +;========================================================================= + +PC_386_CHK PROC NEAR ;AN000;DETERMINE MACHINE TYPE + + PUSH AX ;AN000;SAVE AFFECTED REGS + PUSH BX ;AN000; + PUSH ES ;AN000; + + MOV CS:PC_386,FALSE ;AN000;INITIALIZE TO FALSE + + MOV AH,0C0H ;AN000;RETURN SYSTEM CONFIGURATION + INT 15H ;AN000; + +; $IF NC ;AN000;IF A GOOD RETURN + JC $$IF13 + CMP AH,0 ;AN000;IS IT NEW FORMAT FOR CONFIG. +; $IF E ;AN000;YES + JNE $$IF14 + MOV AL,ES:[BX.BIOS_SD_MODELBYTE] ;AN000;CHECK MODEL + CMP AL,0F8H ;AN000;IS IT A 386 MACHINE? +; $IF E ;AN000;YES + JNE $$IF15 + MOV CS:PC_386,TRUE ;AN000;SIGNAL A 386 +; $ENDIF ;AN000; +$$IF15: +; $ENDIF ;AN000; +$$IF14: +; $ENDIF ;AN000; +$$IF13: + + POP ES ;AN000;RESTORE REGS. + POP BX ;AN000; + POP AX ;AN000; + + RET ;AN000; + +PC_386_CHK ENDP ;AN000; + + + SUBTTL GET_PARMS Parameter Line Scan + PAGE +;-----------------------------------------------------------------------; +;GET_PARMS gets the parameters from the CONFIG.SYS statement ; +; ; +;Register usage: ; +; DS:SI indexes parameter string ; +; AL contains character from parameter string ; +; CX value from GET_NUMBER ; +;-----------------------------------------------------------------------; + ASSUME DS:NOTHING ;DS:BX point to Request Header +GET_PARMS PROC ;get parameters from CONFIG.SYS line + PUSH DS ;save DS + LDS SI,RH.RH0_BPBA ;DS:SI point to all after DEVICE= + ;in CONFIG.SYS line + XOR AL,AL ;not at end of line + +;Skip until first delimiter is found. There may be digits in the path string. + +;DS:SI points to \pathstring\VDISK.SYS nn nn nn +;The character following VDISK.SYS may have been changed to a null (00H). +;All letters have been changed to uppercase. + +GET_PARMS_A: ;skip to DOS delimiter character + CALL GET_PCHAR ;get parameter character into AL + JZ Get_Parms_X_Exit ;get out if end of line encountered + OR AL,AL ;test for null + JZ GET_PARMS_C ; + CMP AL,' ' + JE GET_PARMS_C ; + CMP AL,',' + JE GET_PARMS_C ; + CMP AL,';' + JE GET_PARMS_C ; + CMP AL,'+' + JE GET_PARMS_C ; + CMP AL,'=' + JE GET_PARMS_C ; + CMP AL,TAB + JNE GET_PARMS_A ;skip until delimiter or CR + + + +GET_PARMS_C: + PUSH SI ;save to rescan + MOV CS:EM_SW,0 ;INDICATE NO /E FOUND + JMP GET_SLASH ;see if current character is an slash + +GET_PARMS_D: ;scan for / + CALL GET_PCHAR + JZ GET_PARMS_B ;exit if end of line + +GET_SLASH: ;check for slash + CMP AL,'/' ;found slash? + JNE GET_PARMS_D ;no, continue scan + + CALL GET_PCHAR ;get char following slash + CMP AL,'E' ;don't have to test for lower case E, + ;letters have been changed to upper case + JNE CHECK_FOR_X ;not 'E' ;AN005; + CMP CS:EM_SW,'X' ;Was /X already defined? ;AN005; + JE GET_PARMS_E ;indicate invalid switch ;AN005; + MOV CS:EM_SW,AL ;indicate /E found ;AN005; + JMP SHORT GOT_E_OR_X ;AN005; + ;AN005; +CHECK_FOR_X: ;AN005; + CMP AL,'X' ;don't have to test for lower case X, ;AN005; + ;letters have been changed to upper case ;AN005; + JNE GET_PARMS_E ;not 'X' ;AN005; + CMP CS:EM_SW,'E' ;Was /E already defined? ;AN005; + JE GET_PARMS_E ;indicate invalid switch ;AN005; + MOV CS:EM_SW,'X' ;indicate /X found ;AN005; + ;AN005; +GOT_E_OR_X: ;AN005; + CALL GET_PCHAR ;get char following E or X ;AN005; + CMP AL,':' ;is it a delimeter ? + JNE GET_PARMS_D ;not a ':' + + + CALL GET_MAXSIZE ;get maximum sector size + + + JMP GET_PARMS_D ;continue forward scan + +GET_PARMS_E: ;/ found, not 'E' + OR CS:ERR_FLAG,ERR_SWTCH ;indicate invalid switch character + JMP GET_PARMS_D ;continue scan + + + +GET_PARMS_B: ;now pointing to first delimiter + POP SI ;get pointer, used to rescan for /E + XOR AL,AL ;not at EOL now + CALL GET_PCHAR ;get first character + CALL SKIP_TO_DIGIT ;skip to first digit + +Get_Parms_X_Exit: + + JZ GET_PARMS_X ;found EOL, no digits remain + + CALL GET_NUMBER ;extract digits, convert to binary + MOV CS:BUFF_SIZE,CX ;store buffer size + + CALL SKIP_TO_DIGIT ;skip to next digit + JZ GET_PARMS_X ;found EOL, no digits remain + + CALL GET_NUMBER ;extract digits, convert to binary + MOV CS:BPB_SSZ,CX ;store sector size + + CALL SKIP_TO_DIGIT ;skip to next digit + JZ GET_PARMS_X ;found EOL, no digits remain + + CALL GET_NUMBER ;extract digits, convert to binary + MOV CS:BPB_DIRN,CX ;store number of directory entries + + + +GET_PARMS_X: ;premature end of line + TEST cs:ERR_FLAG,ERR_SWTCH ;was an invalid switch character found? +; $if nz ;yes - set flag to regular VDISK ;an000; dms; + JZ $$IF19 + ; this is consistent with DOS 3.3 + mov cs:EM_SW,0 ;set flag to regular VDISK ;an000; dms; +; $endif ; ;an000; dms; +$$IF19: + POP DS ;restore DS + RET + + +GET_MAXSIZE PROC ;get maximum sector size + + CALL GET_PCHAR ;get next character + CALL CHECK_NUM ;is it a number ? + JZ GET_NEXTNUM ;yes, go get next number + OR CS:ERR_FLAG,ERR_ESIZE ;indicate invalid sector size + RET ; +GET_NEXTNUM: ;get next number + CALL GET_NUMBER ;extract digits and convert to binary + MOV CS:MAXSEC_TRF,CX ;save maximum sector size to transfer + RET +GET_MAXSIZE ENDP + + + +GET_PCHAR PROC ;internal proc to get next character into AL + CMP AL,CR ;carriage return already encountered? + JE GET_PCHAR_X ;don't read past end of line + CMP AL,LF ;line feed already encountered? + JE GET_PCHAR_X ;don't read past end of line + LODSB ;get char from DS:SI, increment SI + CMP AL,CR ;is the char a carriage return? + JE GET_PCHAR_X ;yes, set Z flag at end of line + CMP AL,LF ;no, is it a line feed? +GET_PCHAR_X: ;attempted read past end of line + RET +GET_PCHAR ENDP ;returns char in AL + + +CHECK_NUM PROC ;check AL for ASCII digit + CMP AL,'0' ;< '0'? + JB CHECK_NUM_X ;exit if it is + + CMP AL,'9' ;> '9'? + JA CHECK_NUM_X ;exit if it is + + CMP AL,AL ;set Z flag to indicate numeric +CHECK_NUM_X: + RET ;Z set if numeric, NZ if not numeric +CHECK_NUM ENDP + + +SKIP_TO_DIGIT PROC ;skip to first numeric character + CALL CHECK_NUM ;is current char a digit? + JZ SKIP_TO_DIGIT_X ;if so, skip is complete + + CALL GET_PCHAR ;get next character from line + JNZ SKIP_TO_DIGIT ;loop until first digit or CR or LF + RET ;character is CR or LF + +SKIP_TO_DIGIT_X: + CMP AL,0 ;digit found, force NZ + RET +SKIP_TO_DIGIT ENDP + +C10 DW 10 +GN_ERR DB ? ;zero if no overflow in accumulation + +GET_NUMBER PROC ;convert string of digits to binary value + XOR CX,CX ;accumulate number in CX + MOV CS:GN_ERR,CL ;no overflow yet +GET_NUMBER_A: ;accumulate next digit + SUB AL,'0' ;convert ASCII to binary + CBW ;clear AH + XCHG AX,CX ;previous accumulation in AX, new digit in CL + MUL CS:C10 ;DX:AX := AX*10 + OR CS:GN_ERR,DL ;set GN_ERR <> 0 if overflow + ADD AX,CX ;add new digit from + XCHG AX,CX ;number now in CX + DEC SI ;back up to prior entry + MOV AL,' ' ;blank out prior entry + MOV [SI],AL ; + INC SI ;set to current entry + CALL GET_PCHAR ;get next character + CALL CHECK_NUM ;see if it was numeric + JZ GET_NUMBER_A ;continue accumulating + CMP CS:GN_ERR,0 ;did we overflow? + JE GET_NUMBER_B ;if not, we're done + XOR CX,CX ;return zero (always invalid) if overflow +GET_NUMBER_B: + RET ;number in CX, next char in AL +GET_NUMBER ENDP + +GET_PARMS ENDP + + SUBTTL APPLY_DEFAULTS + PAGE +;-----------------------------------------------------------------------; +; APPLY_DEFAULTS supplies any parameter values that the user ; +; failed to specify ; +;-----------------------------------------------------------------------; + ASSUME DS:CSEG +APPLY_DEFAULTS PROC + XOR AX,AX + CMP BUFF_SIZE,AX ;is buffer size zero? + JNE APPLY_DEFAULTS_A ;no, user specified something + + MOV BUFF_SIZE,DFLT_BSIZE ;supply default buffer size + OR ERR_FLAG,ERR_BSIZE ;indicate buffersize adjusted + +APPLY_DEFAULTS_A: + CMP BPB_SSZ,AX ;is sector size zero? + JNE APPLY_DEFAULTS_B ;no, user specified something + + MOV BPB_SSZ,DFLT_SSZ ;supply default sector size + OR ERR_FLAG,ERR_SSZ ;indicate sector size adjusted + +APPLY_DEFAULTS_B: + CMP BPB_DIRN,AX ;are directory entries zero? + JNE APPLY_DEFAULTS_C ;no, user specified something + + MOV BPB_DIRN,DFLT_DIRN ;supply default directory entries + OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted + +APPLY_DEFAULTS_C: ; + CMP EM_SW,0 ;EXTENDED MEMORY + JE APPLY_DEFAULTS_D ;no, jump around + CMP MAXSEC_TRF,AX ;is maximum sectors zero? + JNE APPLY_DEFAULTS_D ;no, user specified something + + MOV MAXSEC_TRF,DFLT_ESS ;supply default maximum number of + ;sector to transfer + OR ERR_FLAG,ERR_ESIZE ;indicate transfer size adjusted +APPLY_DEFAULTS_D: + RET +APPLY_DEFAULTS ENDP + + SUBTTL DETERMINE_START address of VDISK buffer + PAGE +;-----------------------------------------------------------------------; +; DETERMINE_START figures out the starting address of the VDISK ; +; buffer ; +;-----------------------------------------------------------------------; + ASSUME DS:CSEG +DETERMINE_START PROC + +;If extended memory is NOT being used, the VDISK buffer immediately +;follows the resident code. + +;If extended memory IS being used, START_BUFFER_PARA becomes the +;end of device driver address passed back to DOS. + + MOV AX,CS ;start para of VDISK code + ADD AX,VDISKP ;+ length of resident code + MOV START_BUFFER_PARA,AX ;save as buffer start para + + CMP EM_SW,0 ;IS EXTENDED MEMORY REQUESTED? + JE DETERMINE_START_X ;if not, we're done here + +;-----------------------------------------------------------------------;AN000; +;If EMS is not installed, the calculation to determine the starting address +;of the VDISK will remain the same. + +;If EMS is installed we really don't need this calculation, the EMM will +;manage the expanded memory insuring mutiple VDISKs may reside concurrently. +;-----------------------------------------------------------------------;AN000; + + cmp EM_SW,EMS_Mem ;EMS requested? ;an005; dms; + je Determine_Start_X ;yes - leave routine ;an005; dms; + ;no - continue routine + + clc ;an001; dms;clear carry for INT + MOV AX,EM_MEMSIZE ;an001; dms; get EM memory size + INT EM_INT ;an001; dms; INT 15h + JC Determine_Start_X ;an001; dms; no extended memory installed + or ax,ax ;an001; dms;see if memory returned + jz Determine_Start_X ;an001; dms;signal no memory + + xor dx,dx ;an001; dms;clear dx + sub ax,cs:Buff_Size ;an001; dms;get starting KB location + jc Determine_Start_X ;an001; dms;buffer too large + + mov cs:EM_New_Size,ax ;an001; dms;save new size of EM for later use + mul C1024 ;an001; dms;get total byte count + add ax,cs:Avail_Lo ;an001; dms;add in low word of EM start + adc dl,cs:Avail_Hi ;an001; dms;add in high word of EM start + + mov cs:Avail_Lo,ax ;an001; dms;save new low beginning word + mov cs:Avail_Hi,dl ;an001; dms;save new high beginning word + + mov cs:Start_EM_Lo,ax ;an001; dms;load in new beginning word + mov cs:Start_EM_Hi,dl ;an001; dms;load in new beginning byte + +DETERMINE_START_X: + + RET +DETERMINE_START ENDP + + SUBTTL VALIDATE parameters + PAGE +;-----------------------------------------------------------------------; +; VALIDATE adjusts parameters as necessary ; +;-----------------------------------------------------------------------; +VAL_SSZ_TBL LABEL WORD ;table of valid sector sizes +VAL_SSZ_S DW 128 ;smallest valid sector size + DW 256 +VAL_SSZ_L DW 512 ;largest valid sector size +VAL_SSZ_N EQU ($-VAL_SSZ_TBL)/2 ;number of table entries + + ASSUME DS:CSEG +VALIDATE PROC ;validate parameters +;;ice PUSH DS ;ICE +;;ice push bx ;ICE +;;ice push ax ;ICE + +;;ice mov bx,0140H ;ICE +;;ice xor ax,ax ;ICE +;;ice mov ds,ax ;ICE +;;ice mov ax,word ptr ds:[bx] ;ICE +;;ice mov word ptr ds:[bx],ax ;ICE + +;;ice pop ax ;ICE +;;ice pop bx ;ICE +;;ice POP DS ;ICE + MOV BPB_AUSZ,1 ;initial allocation unit is 1 sector + + CALL VAL_BSIZE ;validate buffer size + + CALL VAL_SSZ ;validate (adjust if necessary) BPB_SSZ + +VALIDATE_A: + AND ERR_FLAG,255-ERR_PASS ;indicate nothing changed this pass + + MOV AX,BPB_SSZ ;sector size + CWD ;clear DX for division + DIV WPARA_SIZE ;sector size/para size + MOV PARAS_PER_SECTOR,AX ;number of paragraphs/sector + + mov ax,EMS_Page_Size ;an002; dms;EMS page size + xor dx,dx ;an002; dms;clear high word + div BPB_SSZ ;an002; dms;get sectors/page + mov Sect_Per_Page,ax ;an002; dms;save sectors/page + +;;;;; MOV AX,BPB_SSZ ;AN000; Sector size +;;;;; xor dx,dx ;an001; clear high word +;;;;; DIV DOS_Page_Size_Word ;an001; Sector size/page size +;;;;; MOV SECT_PER_PAGE,AX ;an001; Number of sectors/page + + MOV AX,BUFF_SIZE ;requested buffersize in KB + MUL C1024 ;DX:AX = buffer size in bytes + DIV BPB_SSZ ;/sector size = # sectors + MOV BPB_SECN,AX ;store number of sectors + + CALL VAL_DIRN ;validate number of directory entries + + TEST ERR_FLAG,ERR_PASS ;may have reset sector size + JNZ VALIDATE_A ;recompute directory & FAT sizes + + CALL VAL_FAT ;compute FAT entries, validity test + + TEST ERR_FLAG,ERR_PASS ;if cluster size altered this pass + JNZ VALIDATE_A ;recompute directory & FAT sizes + +;Make certain buffer size is large enough to contain: +; boot sector(s) +; FAT sector(s) +; directory sector(s) +; at least 1 data cluster + + MOV AL,BPB_FATN ;number of FAT copies + CBW ;clear AH + MUL BPB_FATSZ ;* sectors for 1 FAT = FAT sectors + ADD AX,BPB_RES ;+ reserved sectors + ADD AX,DIR_SECTORS ;+ directory sectors + MOV CL,BPB_AUSZ ;get sectors/cluster + XOR CH,CH ;CX = sectors in one cluster + ADD AX,CX ;+ one data cluster + CMP BPB_SECN,AX ;compare with sectors available + JAE VALIDATE_X ;jump if enough sectors + + CMP DIR_SECTORS,1 ;down to 1 directory sector? + JBE VALIDATE_C ;can't let it go below 1 + + MOV AX,BPB_SSZ ;sector size + CWD ;clear DX for division + DIV DIRE_SIZE ;sectorsize/dir entry size = entries/sector + SUB BPB_DIRN,AX ;reduce directory entries by 1 sector + + OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted + JMP VALIDATE_A ;retry with new directory entries number + +VALIDATE_C: ;not enough space for any VDISK + OR ERR_FLAG,ERR_SYSSZ +VALIDATE_X: + RET + + SUBTTL VAL_BSIZE Validate buffer size + PAGE +;-----------------------------------------------------------------------; +; VAL_BSIZE adjusts the buffer size as necessary ; +;-----------------------------------------------------------------------; +VAL_BSIZE PROC + CALL GET_MSIZE ;determine memory available to VDISK + ;returns available KB in AX + OR AX,AX ;is any memory available at all? + JNZ VAL_BSIZE_B ;yes, continue + + OR ERR_FLAG,ERR_SYSSZ ;indicate system too small for VDISK + MOV BUFF_SIZE,1 ;set up minimal values to continue init + MOV AX,VAL_SSZ_S ;smallest possible sector size + MOV BPB_SSZ,AX + MOV BPB_DIRN,4 ;4 directory entries + RET + +VAL_BSIZE_B: ;some memory is available + CMP AX,BUFF_SIZE ;is available memory >= requested? + JAE VAL_BSIZE_C ;if so, we're done + + MOV BUFF_SIZE,AX ;give all available memory + mov cs:EM_New_Size,0 ;an001; dms;save new size of EM for later use + mov ax,cs:Avail_Lo ;an001; dms;get low word of EM start + mov dl,cs:Avail_Hi ;an001; dms;get high byte of EM start + + mov cs:Start_EM_Lo,ax ;an001; dms;load in new beginning word + mov cs:Start_EM_Hi,dl ;an001; dms;load in new beginning byte + OR ERR_FLAG,ERR_BSIZE ;indicate buffersize adjusted +VAL_BSIZE_C: + + + RET + + +GET_MSIZE PROC ;determine memory available to VDISK + ;returns KB available in AX + CMP EM_SW,0 ;EXTENDED MEMORY? + JE GET_MSIZE_2 ;use non-extended memory routine + + cmp EM_SW,EM_Mem ;Extended memory requested? ;an005; dms; + je Use_Extended_Support ;yes ;an005; dms; + ;no - check for EMS availability + + + CALL EMS_CHECK ;AN000; Check if EMS is installed + JC GET_MSIZE_Z ;AN000; Yes, it is installed but in error + ; then notify caller by setting AX to zero + CMP AH,NOT EMS_INSTALLED_FLAG ;AN000; + JE Get_Msize_Z ;ac005; flag an error occurred + MOV EM_SW2,AH ;AN000; Set EMS flag + CALL EMS_GET_PAGES ;AN000; Get count of total number of pages + xor dx,dx ;an002; clear high word + MUL DOS_Page_Size_Word ;ac002; Number of pages * KB per page + RET ;AN000; Return with AX = number of whole free kilobytes + ; +USE_EXTENDED_SUPPORT: ;AN000; No, EMS is not installed + + MOV AX,EM_MEMSIZE ;function code to AH + INT EM_INT ;get extended memory size in AX + JC GET_MSIZE_Z ;if error, no extended memory installed + or ax,ax ;an000; dms;see if memory returned + jz GET_MSIZE_Z ;an000; dms;signal no memory + + RET + +GET_MSIZE_2: ;non-extended memory size determination + +;Compute AX = total system size, - (VDISK end address + 64KB) + + MOV AX,START_BUFFER_PARA ;paragraph end of VDISK code + XOR DX,DX ;clear for division + DIV PARA_PER_KB ;KB address of load point + ADD DX,0FFFFH ;round upward to KB boundary + ADC AX,MIN_MEMORY_LEFT ;pick up CY and the 64KB we should leave + PUSH AX ;save across interrupt + INT MEM_SIZE ;get total system size + POP DX ;amount of total that we can't use + SUB AX,DX ;available space to VDISK + JNC GET_MSIZE_X ;exit if positive + +GET_MSIZE_Z: + XOR AX,AX ;indicate no memory available +GET_MSIZE_X: ;exit from memory size determination + RET +GET_MSIZE ENDP + +VAL_BSIZE ENDP + + + +EMS_CHECK PROC NEAR ;AN000; + + CALL EMS_CHECK1 ;AN000; SEE IF EMS INSTALLED + JNC EMS_INSTALLED ;AN000; No, + MOV AH,NOT EMS_INSTALLED_FLAG ;AN000; Flag EMS not installed + CLC ;AN000; Make sure carry is Clear + JMP SHORT EMS_CHECK_EXIT ;AN000; Leave check routine + +EMS_INSTALLED: ;AN000; Yes, + + push es ;an000; save es - call destroys it + push di ;an006; save di + + mov ah,EMS_2F_Handler ;an006;see if our 2Fh is there + xor al,al ;an006; + int 2Fh ;an006; + cmp al,0ffh ;an006;2Fh handler there? +; $if e ;an006;yes + JNE $$IF21 + mov ah,EMS_2F_Handler ;an006;get EMS page for VDISK + mov al,0FFh ;an006; + mov di,0FEh ;an006; + int 2Fh ;ac006; + + or ah,ah ;an006;page available? +; $if z ;an006;yes + JNZ $$IF22 + mov cs:EMS_Frame_Addr,es ;an006;save segment value + mov cs:DOS_Page,di ;an006;save physical page # + clc ;an006;flag memory available + mov ah,EMS_INSTALLED_FLAG ;an000;signal EMS here +; $else ;an006;no memory avail. + JMP SHORT $$EN22 +$$IF22: + mov ah,not EMS_INSTALLED_FLAG ;an000;signal no EMS + stc ;an006;flag it +; $endif ;an006; +$$EN22: +; $else + JMP SHORT $$EN21 +$$IF21: + mov ah,not EMS_INSTALLED_FLAG ;AN000;signal no EMS + stc ;an006;signal not there +; $endif +$$EN21: + + pop di ;an006;restore di + pop es ;an000;restore es + +EMS_Check_Exit: + + RET ;AN000; Return + +EMS_CHECK ENDP ;AN000; + + + +;========================================================================= +; EMS_CHECK1 : THIS MODULE DETERMINES WHETHER OR NOT EMS IS +; INSTALLED FOR THIS SESSION. +; +; INPUTS : NONE +; +; OUTPUTS : ES:BX - FRAME ARRAY +; CY - EMS NOT AVAILABLE +; NC - EMS AVAILABLE +;========================================================================= + +EMS_CHECK1 PROC NEAR ;AN000;EMS INSTALL CHECK + + push ds ;an000;save ds - we stomp it + mov ax,00h ;an000;set ax to 0 + mov ds,ax ;an000;set ds to 0 + cmp ds:word ptr[067h*4+0],0 ;an000;see if int 67h is there + pop ds ;an000;restore ds +; $IF NE ;AN000;EMS VECTOR CONTAINS DATA + JE $$IF27 + MOV AH,EMS_STATUS ;AN000;see if EMS installed + XOR AL,AL ;AN000;CLEAR AL + INT EMS_INT ;AN000; + OR AH,AH ;AN000;EMS INSTALLED? +; $IF Z ;AN000;YES + JNZ $$IF28 + MOV AH,EMS_VERSION ;AN000;GET VERSION NUMBER + XOR AL,AL ;AN000;CLEAR AL + INT EMS_INT ;AN000; + CMP AL,EMS_VERSION_LEVEL ;AN000;CORRECT VERSION? +; $IF AE ;AN000;YES + JNAE $$IF29 + CLC ;AN000;FLAG IT AS GOOD EMS +; $ELSE ;AN000; + JMP SHORT $$EN29 +$$IF29: + STC ;AN000;BAD EMS +; $ENDIF ;AN000; +$$EN29: +; $ELSE ;AN000; + JMP SHORT $$EN28 +$$IF28: + STC ;AN000;EMS NOT INSTALLED +; $ENDIF ;AN000; +$$EN28: +; $ELSE ;AN000; + JMP SHORT $$EN27 +$$IF27: + STC ;AN000;EMS VECTOR NOT THERE +; $ENDIF ;AN000; +$$EN27: + + RET ;AN000;RETURN TO CALLER + +EMS_CHECK1 ENDP ;AN000; + + +EMS_GET_PAGES PROC NEAR ;AN000; + + MOV AH,EMS_GET_NUM_PAGES ;AN000; Query EMS for page count + INT EMS_INT ;AN000; + OR AH,AH ;AN000; Has EMS returned page count? + JNZ EMS_GET_ERROR ;AN000; Yes, + MOV AX,BX ;AN000; Get number of pages + RET ;AN000; + +EMS_GET_ERROR: ;AN000; + STC ;AN000; + RET ;AN000; + +EMS_GET_PAGES ENDP + + SUBTTL VAL_SSZ Validate Sector Size + PAGE +;-----------------------------------------------------------------------; +; VAL_SSZ validates sector size, adjusting if necessary ; +;-----------------------------------------------------------------------; +VAL_SSZ PROC ;validate sector size + CMP CS:EM_SW,0 ;EXTENDED MEMORY? + JE VAL_SSZ_ST ;no,go check sector size + MOV BX,MAXSEC_TRF ;move number of sectors to transfer + CMP BX,1 ;> or equal to 1 ? + JB DFLT_TRF ;set default if it is + CMP BX,8 ;> than 8 ? + JA DFLT_TRF ;set default if it is + JMP VAL_SSZ_ST ;continue processing + +DFLT_TRF: ;set default + MOV MAXSEC_TRF,DFLT_ESS ; + MOV BX,MAXSEC_TRF ; + OR CS:ERR_FLAG,ERR_ESIZE ;indicate transfer size adjusted + +VAL_SSZ_ST: ;validate sector size + MOV MAX_CNT,BX ;initialize maximum number of sectors + ;to transfer for extended memory case + MOV BX,BPB_SSZ ;requested sector size + MOV CX,VAL_SSZ_N ;number of table entries + MOV SI,OFFSET VAL_SSZ_TBL ;DS:SI point to table start +VAL_SSZ_A: + LODSW ;get table entry, step table pointer + CMP AX,BX ;is value in table? + JE VAL_SSZ_X ;exit if value found + LOOP VAL_SSZ_A ;loop until table end + + MOV BX,DFLT_SSZ ;get default sector size + MOV BPB_SSZ,BX ;set sector size to default value + OR ERR_FLAG,ERR_SSZ ;indicate sector size adjusted +VAL_SSZ_X: + +;Compute the maximum number of sectors that can be moved in 64KB (less one) +;Restricting moves to this amount avoids 64KB boundary problems. + + CMP CS:EM_SW,0 ;EXTENDED MEMORY? + JNE SIZE_DONE ;yes, we are done + XOR DX,DX + MOV AX,0FFFFH ;64KB - 1 + DIV BX ;/sector size + MOV MAX_CNT,AX ;max sectors in one move +SIZE_DONE: + RET +VAL_SSZ ENDP + + SUBTTL VAL_DIRN Validate number of directory entries + PAGE +;-----------------------------------------------------------------------; +; VAL_DIRN validates and adjusts the number of directory entries. ; +; ; +; Minimum is MIN_DIRN, maximum is MAX_DIRN. If outside these ; +; limits, DFLT_DIRN is used. ; +; ; +; The number of directory entries is rounded upward to fill ; +; a sector ; +;-----------------------------------------------------------------------; +VAL_DIRN PROC + MOV AX,BPB_DIRN ;requested directory entries + CMP AX,MIN_DIRN ;if less than minimum + JB VAL_DIRN_A ;use default instead + + CMP AX,MAX_DIRN ;if <= maximum + JBE VAL_DIRN_B ;accept value as provided + +VAL_DIRN_A: + MOV AX,DFLT_DIRN ;use default directory entries + OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted +VAL_DIRN_B: ;AX is number of directory entries + MUL DIRE_SIZE ;* 32 = bytes of directory requested + DIV BPB_SSZ ;/ sector size = # of directory sectors + OR DX,DX ;test remainder for zero + JZ VAL_DIRN_C ;jump if exact fit + + INC AX ;increment directory sectors + OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted +VAL_DIRN_C: ;make sure enough sectors available + MOV DX,BPB_SECN ;total sectors on media + SUB DX,BPB_RES ;less reserved sectors + SUB DX,2 ;less minimum FAT and 1 data sector + CMP AX,DX ;if directory sectors <= available + JLE VAL_DIRN_D ;use requested amount + + MOV AX,1 ;use only one directory sector + OR ERR_FLAG,ERR_DIRN ;indicate directory entries adjusted +VAL_DIRN_D: + MOV DIR_SECTORS,AX ;save number of directory sectors + MUL BPB_SSZ ;dir sectors * sector size = dir bytes + DIV DIRE_SIZE ;dir bytes / entry size = entries + MOV BPB_DIRN,AX ;store adjusted directory entries + RET +VAL_DIRN ENDP + + SUBTTL VAL_FAT Validate File Allocation Table (FAT) + PAGE +;-----------------------------------------------------------------------; +;VAL_FAT computes: ; +;BPB_FATSZ, the number of sectors required per FAT copy ; +; ; +;Each FAT entry is 12 bits long, for a maximum of 4095 FAT entries. ; +;(A few FAT entries are reserved, so the highest number of FAT entries ; +;we permit is 0FE0H.) With large buffer sizes and small sector sizes, ; +;we have more allocation units to describe than a 12-bit entry will ; +;describe. If the number of FAT entries is too large, the sector size ; +;is increased (up to a maximum of 512 bytes), and then the allocation ; +;unit (cluster) size is doubled, until we have few enough allocation ; +;units to be properly described in 12 bits. ; +; ; +;This computation is slightly conservative in that the FAT entries ; +;necessary to describe the FAT sectors are included in the computation. ; +;-----------------------------------------------------------------------; +VAL_FAT PROC + MOV AX,BPB_SECN ;total number of sectors + SUB AX,BPB_RES ;don't count boot sector(s) + SUB AX,DIR_SECTORS ;don't count directory sectors + + CMP AX,0000h ;an000; dms; fix ptm 112; any left? + JA VAL_FAT_A ;an000; dms; fix ptm 112; yes + +;;;;; JG VAL_FAT_A ;jump if some remaining + MOV BPB_SSZ,DFLT_SSZ ;force default sector size + OR ERR_FLAG,ERR_SSZ+ERR_PASS ;indicate sector size adjusted + JMP SHORT VAL_FAT_X ;recompute all values +VAL_FAT_A: + XOR DX,DX ;clear DX for division + MOV CL,BPB_AUSZ ;CX = sectors/cluster + XOR CH,CH + DIV CX ;whole number of clusters in AX + ADD DX,0FFFFH ;set carry if remainder + ADC AX,0 ;increment AX if remainder + CMP AX,MAX_FATE ;number of FAT entries too large? + JBE VAL_FAT_C ;no, continue + + MOV AX,BPB_SSZ ;pick up current sector size + CMP AX,VAL_SSZ_L ;already at largest permitted? + JE VAL_FAT_B ;yes, can't make it any larger + + SHL BPB_SSZ,1 ;double sector size + OR ERR_FLAG,ERR_SSZB ;indicate sector size adjusted + JMP SHORT VAL_FAT_X ;recompute all sizes with new BPBSSZ + +VAL_FAT_B: ;sector size is at maximum + SHL BPB_AUSZ,1 ;double allocation unit size + OR ERR_FLAG,ERR_PASS ;indicate another pass required + JMP SHORT VAL_FAT_X ;recompute values + +VAL_FAT_C: ;FAT size = 1.5 * number of clusters + MOV CX,AX ;number of clusters + SHL AX,1 ;* 2 + ADD AX,CX ;* 3 + SHR AX,1 ;* 1.5 + ADC AX,3 ;add 3 bytes for first 2 FAT entries + ;(media descriptor and FFFFH), and CY + XOR DX,DX ;clear DX for division + DIV BPB_SSZ ;FAT size/sector size + ADD DX,0FFFFH ;set carry if remainder + ADC AX,0 ;round upward + MOV BPB_FATSZ,AX ;number of sectors for 1 FAT copy +VAL_FAT_X: + RET +VAL_FAT ENDP + + +VALIDATE ENDP + + SUBTTL COPY_BPB Copy BPB to Boot Record + PAGE +;-----------------------------------------------------------------------; +; COPY_BPB copies the BIOS Parameter Block (BPB) ; +; to the VDISK Boot Record ; +;-----------------------------------------------------------------------; + ASSUME DS:CSEG +COPY_BPB PROC ;Copy BBP to Boot Record + PUSH DS + POP ES ;set ES = DS + + MOV CX,BPB_LEN ;length of BPB + MOV SI,OFFSET BPB ;source offset + MOV DI,OFFSET BOOT_BPB ;target offset + REP MOVSB ;copy BPB to boot record + RET +COPY_BPB ENDP + + SUBTTL VERIFY_EXTENDER + PAGE +;-----------------------------------------------------------------------; +; VERIFY_EXTENDER makes sure that if an Expansion Unit is ; +; installed, the memory size switches on the Extender Card ; +; are correctly set. ; +;-----------------------------------------------------------------------; + + + ASSUME DS:CSEG +EXT_P210 EQU 0210H ;write to latch expansion bus data + ;read to verify expansion bus data +EXT_P213 EQU 0213H ;Expansion Unit status + +VERIFY_EXTENDER PROC + + NOP + + MOV DX,EXT_P210 ;Expansion bus data port address + + MOV AX,5555H ;set data pattern + OUT DX,AL ;write 55H to control port + PUSH DX + POP DX + + JMP SHORT $+2 ;Let the I/O circuits catch up + IN AL,020h ;Clear the CMOS bus drivers! + + IN AL,DX ;recover data + CMP AH,AL ;did we recover the same data? + JNE VERIFY_EXTENDER_X ;if not, no extender card + + NOT AX ;set AX = 0AAAAH + OUT DX,AL ;write 0AAH to control port + PUSH DX ;load data line + POP DX ;load data line + + JMP SHORT $+2 ;Let the I/O circuits catch up + IN AL,020h ;Clear the CMOS bus drivers! + + IN AL,DX ;recover data + CMP AH,AL ;did we recover the same data? + JNE VERIFY_EXTENDER_X ;if not, no extender card + +;Expansion Unit is present. + +;Determine what the switch settings should be on the Extender Card + + INT MEM_SIZE ;get system memory size in KB in AX + ADD AX,63D ;memory size + 63K + MOV CL,6 ;2^6 = 64 + SHR AX,CL ;divide by 64 + ;AX is highest segment address + MOV AH,AL ;save number of segments + +;Read Expander card switch settings + + MOV DX,EXT_P213 ;expansion unit status + IN AL,DX ;read status + ;bits 7-4 (hi nibble) are switches + MOV CL,4 ;shift count + SHR AL,CL ;shift switches to bits 3-0 of AL + + CMP AH,AL ;do switches match memory size? + JE VERIFY_EXTENDER_X ;yes, exit normally + + OR ERR_FLAG,ERR_EXTSW ;indicate switch settings are wrong + +VERIFY_EXTENDER_X: + RET +VERIFY_EXTENDER ENDP + + SUBTTL UPDATE_AVAIL + PAGE +;-----------------------------------------------------------------------; +; UPDATE_AVAIL updates the address of the first byte in extended ; +; memory not used by any VDISK buffer ; +;-----------------------------------------------------------------------; +;If EMS is installed, we must allocate memory here and obtain the ; +;handle which we will use throughout our existance. AVAIL_LO and _HI ; +;really mean nothing to us. ; +;-----------------------------------------------------------------------; + +UPDATE_AVAIL PROC + MOV AX,BUFF_SIZE ;number of KB of VDISK buffer + + CMP EM_SW2,EMS_INSTALLED_FLAG ;AN000; Is EMS installed? + JNE USE_INT15_LOGIC ;ac006; Yes, + xor dx,dx ;an003; clear high word + div DOS_Page_Size_Word ;ac003; Calculate number of pages needed + or dx,dx ;an004; remainder? +; $if nz ;an004; yes + JZ $$IF36 + inc ax ;an004; need 1 extra page +; $endif ;an004; +$$IF36: + MOV BX,AX ;AN000; Prepare for EMS call + + MOV AH,EMS_ALLOC_PAGES ;AN000; Allocate requested pages + INT EMS_INT ;AN000; + OR AH,AH ;AN000; Was there an error allocating? + JNZ ALLOC_ERROR ;AN000; No, + MOV EMS_HANDLE,DX ;AN000; Save EMS handle for this VDISK + call EMS_Build_Handle_Name ;an000; dms; + + RET ;AN000; + ;AN000; +ALLOC_ERROR: ;AN000; + MOV ERR_FLAG,EMS_ALLOC_ERROR ;AN000; ?????? *RPS + RET ;AN000; + ;AN000; +USE_INT15_LOGIC: ;AN000; + + call Modify_CMOS_EM ;an001; dms;adjust EM for new size + + RET +UPDATE_AVAIL ENDP + + +;========================================================================= +; Modify_CMOS_EM : This routine modifies the size of extended +; memory. By modifying the size of extended +; memory other users will not have the potential +; to overlay a VDISK residing in EM. +; +; Inputs : EM_New_Size - The new size that EM will be after +; creation of this VDISK. +; Outputs : Modified data in CMOS for EM. Bytes 17h & 18h at +; port 71h. +;========================================================================= + +Modify_CMOS_EM Proc Near ;an001; dms; + + push ax ;an001; dms;save ax + call Steal_Int15 ;an001; dms;get INT 15h vector + + mov ax,word ptr cs:EM_New_Size ;an001; dms;transfer new size + mov word ptr cs:EM_KSize,ax ;an001; dms;set EM size to new size + pop ax ;an001; dms;restore ax + + ret ;an001; dms; + +Modify_CMOS_EM endp ;an001; dms; + +;========================================================================= +; EMS_Build_Handle_Name - This routine will build an EMS handle's +; name as follows: +; VDISK D: +; +; Inputs : DX - Handle to have associated name +; +; Outputs : Handle name +;========================================================================= + +EMS_Build_Handle_Name proc near ;an000; dms; + + push si ;an000; dms;save si + push cx ;an000; dms;save cx + + push ds ;an000; dms;save ds + push bx ;an000; dms;save bx + lds bx,RH_Ptra ;an000; dms;point to request header + mov ch,RH.RH0_Driv ;an000; dms;get drive number + add ch,'A' ;an000; dms;convert to drive letter + pop bx ;an000; dms;restore bx + pop ds ;an000; dms;restore ds + + mov si,offset VDISK_Name ;an000; dms;point to "VDISK " literal + mov byte ptr [si+6],ch ;an000; dms;put drive letter in string + mov byte ptr [si+7],":" ;an000; dms;colon terminate it + + mov ax,EMS_Set_Handle_Name ;an000; dms;set the handle's name + int EMS_INT ;an000; dms; + + pop cx ;an000; dms;restore cx + pop si ;an000; dms;restore si + + ret ;an000; dms; + +EMS_Build_Handle_Name endp ;an000; dms; + + SUBTTL FORMAT_VDISK + PAGE +;-----------------------------------------------------------------------; +; This Request Header is used by MOVE_VDISK to move the ; +; first few sectors of the virtual disk (boot, FAT, and ; +; Directory) into extended memory. ; +;-----------------------------------------------------------------------; + +MOVE_RH DB MOVE_RH_L ;length of request header + DB 0 ;sub unit + DB 8 ;output operation + DW 0 ;status + DQ ? ;reserved for DOS + DB ? ;media descriptor byte +MOVE_RHO DW ? ;offset of data transfer address +MOVE_RHS DW ? ;segment of data transfer address +MOVE_RHCNT DW ? ;count of sectors to transfer + DW 0 ;starting sector number +MOVE_RH_L EQU $-MOVE_RH ;length of request header + +;-----------------------------------------------------------------------; +; FORMAT_VDISK formats the boot sector, FAT, and directory of an ; +; extended memory VDISK in storage immediately following ; +; VDISK code, in preparation for moving to extended memory. ; +;-----------------------------------------------------------------------; +FORMAT_VDISK PROC ;format boot record, FATs and directory + + MOV AX,CS ;compute 20-bit address + MUL WPARA_SIZE ;16 * segment + ADD AX,OFFSET MSGEND ;+ offset + ADC DL,0 ;pick up carry + ADD AX,STACK_SIZE ;plus stack size + ADC DL,0 ;pick up carry + + DIV WPARA_SIZE ;split into segment(AX)&offset(DX) + MOV MOVE_RHS,AX ;save in Request Header for move + MOV MOVE_RHO,DX + + MOV DI,DX ;offset to DI + MOV ES,AX ;segment to ES + +;copy the boot record + + MOV SI,OFFSET BOOT_RECORD ;point to source field + MOV AX,BPB_RES ;number of reserved sectors + MUL BPB_SSZ ;* sector size = length of boot records + MOV CX,AX ;length to CX for move + REP MOVSB ;move boot record(s) + +;format the FAT(s) + + MOV CL,BPB_FATN ;number of FATs + XOR CH,CH +FORMAT_VDISK_A: ;set up one FAT + PUSH CX ;save loop counter on stack + MOV AL,BPB_MCB ;media control byte + STOSB ;store media control byte, increment DI + MOV AX,0FFFFH ;bytes 2 and 3 of FAT are 0FFH + STOSW + MOV AX,BPB_FATSZ ;number of sectors per FAT + MUL BPB_SSZ ;* sector size = length of FAT in bytes + SUB AX,3 ;less the 3 bytes we've stored + MOV CX,AX ;count to CX + XOR AX,AX + REP STOSB ;clear remainder of FAT + POP CX ;get loop counter off stack + LOOP FORMAT_VDISK_A ;loop for all copies of the FAT + +;Format the directory + + MOV SI,OFFSET VOL_LABEL ;point to volume label + MOV CX,VOL_LABEL_LEN ;length of volume directory entry + REP MOVSB ;move volume id to 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 ;CX = length of rest of directory + XOR AX,AX + REP STOSB ;clear directory to nulls + RET +FORMAT_VDISK ENDP + + SUBTTL STEAL_INT15 + PAGE +;-----------------------------------------------------------------------; +; STEAL_INT15 changes the INT 15H vector to point to this VDISK ; +; so that subsequent calls to INT15H may determine the actual ; +; size of EM after VDISK's allocation of it. ; +;-----------------------------------------------------------------------; +STEAL_INT15 PROC + PUSH DS + XOR AX,AX + MOV DS,AX ;set DS = 0 + ASSUME DS:INT_VEC + CLI ;disable interrupts + LES DI,DS:EM_VEC ;get original vector's content + MOV CS:INTV15O,DI ;save original vector + MOV CS:INTV15S,ES + MOV DS:EM_VECO,OFFSET VDISK_INT15 ;offset of new INT routine + MOV DS:EM_VECS,CS ;segment of new INT routine + STI ;enable interrupts again + POP DS ;restore DS + RET +STEAL_INT15 ENDP + + + + SUBTTL MOVE_VDISK + PAGE +;-----------------------------------------------------------------------; +; MOVE_VDISK moves the formatted boot sector, FAT, and directory ; +; into extended memory. ; +;-----------------------------------------------------------------------; + +MOVE_VDISK PROC + MOV AL,cs:BPB_FATN ;number of FAT copies + CBW ;clear AH + MUL cs:BPB_FATSZ ;number of FAT sectors + ADD AX,cs:BPB_RES ;+ reserved sectors + ADD AX,cs:DIR_SECTORS ;+ directory sectors + MOV cs:MOVE_RHCNT,AX ;store as I/O length + + MOV BX,OFFSET MOVE_RH ;DS:BX point to request header + PUSH DS ;make sure DS gets preserved + CALL INOUT ;move to extended memory + POP DS + RET +MOVE_VDISK ENDP + + SUBTTL FILL_RH Fill in Request Header + PAGE +;-----------------------------------------------------------------------; +; FILL_RH fills in the Request Header returned to DOS ; +;-----------------------------------------------------------------------; + ASSUME DS:CSEG +FILL_RH PROC ;fill in INIT Request Header fields + MOV CX,START_BUFFER_PARA ;segment end of VDISK resident code + MOV AX,PARAS_PER_SECTOR ;paragraphs per sector + MUL BPB_SECN ;* number of sectors + ADD AX,CX ;+ starting segment + MOV DX,AX ;DX is segment of end VDISK buffer + CMP EM_SW,0 ;AC000; DMS; IF EM NOT REQUESTED +; $IF NE ;AN000; DMS; EM REQUESTED + JE $$IF38 + MOV DX,CX ;AN000; DMS;END OF CODE SEGMENT ADDR +; $ENDIF ;AN000; DMS; +$$IF38: + +FILL_RH_A: ;DX is proper ending segment address + MOV AL,1 ;number of units + test CS:err_flag2,err_baddos + jnz dont_install + + TEST ERR_FLAG,ERR_SYSSZ+ERR_EXTSW ;if bypassing install + JZ FILL_RH_B ;jump if installing driver + +dont_install: + MOV DX,CS ;segment of end address + XOR AL,AL ;number of units is zero +FILL_RH_B: + PUSH DS ;preserve DS + LDS BX,RH_PTRA ;get Request Header addr in DS:BX + MOV RH.RH0_NUN,AL ;store number of units (0 or 1) + MOV RH.RH0_ENDO,0 ;end offset is always zero + MOV RH.RH0_ENDS,DX ;end of VDISK or end of buffer + MOV RH.RH0_BPBO,OFFSET BPB_PTR + MOV RH.RH0_BPBS,CS ;BPB array address + POP DS ;restore DS + RET +FILL_RH ENDP + + SUBTTL WRITE_MESSAGES and associated routines + PAGE +;-----------------------------------------------------------------------; +; WRITE_MESSAGE writes a series of messages to the standard ; +; output device showing the VDISK parameter values actually used. ; +;-----------------------------------------------------------------------; + + ASSUME DS:CSEG +WRITE_MESSAGES PROC ;display all messages + + + test cs:err_flag2,err_baddos ;AN000; + JZ DISPLAY_ALL_MESSAGES ;AN000; + RET + +DISPLAY_ALL_MESSAGES: ;AN000; No, then display messages + + PUSH DS ;preserve DS + LDS BX,RH_PTRA ;get Request Header Address + MOV CL,RH.RH0_DRIV ;get drive code + ADD CL,'A' ;convert to drive letter + POP DS ;restore DS + + MOV AX,VDISK_TITLE ;AN000; 'VDISK Version 3.3 virtual disk $' + LEA SI,TITLE_SUBLIST ;AN000; Specify SUBLIST to use for replacement + MOV DRIVE_CODE,CL ;AN000; Save drive code + MOV CX,ONE_REPLACE ;AN000; Notify SYSDISPMSG of 1 replacement + CALL DISPLAY_MESSAGE ;AN000; Display the message + JNC WRITE_MESSAGES_A ;AN000; Was there an error? + JMP SYSDISP_ERROR ;AN000; YES, display the extended error + +;If any of the user specified values has been adjusted, issue an +;appropriate message + +WRITE_MESSAGES_A: ;AN000; NO, + TEST ERR_FLAG,ERR_BSIZE ;was buffersize adjusted? + JZ WRITE_MESSAGES_B ;if not, skip message + + MOV AX,BUFFER_ADJUSTED ;AN000; "Buffer size adjusted",CR,LF + MOV CX,NO_REPLACE ;AN000; Notify SYSDISPMSG of no replacement + CALL DISPLAY_MESSAGE ;AN000; Display the message + +WRITE_MESSAGES_B: ;AN000; NO, + TEST ERR_FLAG,ERR_SSZ ;was sector size adjusted? + JZ WRITE_MESSAGES_C ;if not, skip message + + MOV AX,SECTOR_ADJUSTED ;AN000; "Sector size adjusted",CR,LF + MOV CX,NO_REPLACE ;AN000; Notify SYSDISPMSG of no replacement + CALL DISPLAY_MESSAGE ;AN000; Display the message + JNC WRITE_MESSAGES_C ;AN000; Was there an error? + JMP SYSDISP_ERROR ;AN000; YES, display the extended error + +WRITE_MESSAGES_C: + TEST ERR_FLAG,ERR_DIRN ;were directory entries adjusted? + JZ WRITE_MESSAGES_D0 ;if not, skip message + + MOV AX,DIR_ADJUSTED ;AN000; "Directory entries adjusted",CR,LF + MOV CX,NO_REPLACE ;AN000; Notify SYSDISPMSG of no replacement + CALL DISPLAY_MESSAGE ;AN000; Display the message + JNC WRITE_MESSAGES_D0 ;AN000; Was there an error? + JMP SYSDISP_ERROR ;AN000; YES, display the extended error + +WRITE_MESSAGES_D0: + TEST ERR_FLAG,ERR_ESIZE ;was transfer size adjusted? + JZ WRITE_MESSAGES_D ;if not, skip message + + MOV AX,TRANS_ADJUSTED ;AN000; "Transfer size adjusted",CR,LF + MOV CX,NO_REPLACE ;AN000; Notify SYSDISPMSG of no replacement + CALL DISPLAY_MESSAGE ;AN000; Display the message + JNC WRITE_MESSAGES_D ;AN000; Was there an error? + JMP SYSDISP_ERROR ;AN000; YES, display the extended error + +WRITE_MESSAGES_D: + TEST ERR_FLAG,ERR_SWTCH ;was an invalid switch character found? + JZ WRITE_MESSAGES_E ;if not, skip message + + MOV AX,INVALID_SW_CHAR ;AN000; "Invalid switch character",CR,LF + MOV CX,NO_REPLACE ;AN000; Notify SYSDISPMSG of no replacement + CALL DISPLAY_MESSAGE ;AN000; Display the message + JNC WRITE_MESSAGES_E ;AN000; Was there an error? + JMP SYSDISP_ERROR ;AN000; YES, display the extended error + +WRITE_MESSAGES_E: + TEST ERR_FLAG,ERR_SYSSZ ;is system size too small to install? + JZ WRITE_MESSAGES_F ;if not, bypass error message + + MOV AX,VDISK_NOT_INST ;AN000; "VDISK not installed - " + MOV CX,NO_REPLACE ;AN000; Notify SYSDISPMSG of no replacement + CALL DISPLAY_MESSAGE ;AN000; Display the message + MOV AX,SYS_TOO_SMALL ;AN000; "Insufficient memory",CR,LF + CALL DISPLAY_MESSAGE ;AN000; Display the message + JNC WRITE_MESSAGES_RET ;AN000; Was there an error? + JMP SYSDISP_ERROR ;AN000; YES, display the extended error +WRITE_MESSAGES_RET: + RET ;skip messages showing adjusted sizes + +WRITE_MESSAGES_F: + TEST ERR_FLAG,ERR_EXTSW ;extender card switches wrong? + JZ WRITE_MESSAGES_G ;if not, bypass error message + + MOV AX,VDISK_NOT_INST ;AN000; "VDISK not installed - " + MOV CX,NO_REPLACE ;AN000; Notify SYSDISPMSG of no replacement + CALL DISPLAY_MESSAGE ;AN000; Display the message + MOV AX,EXTEND_CARD_WRONG ;AN000; "Extender Card switches",CR,LF,"do not match system memory size",CR,LF,CR,LF + CALL DISPLAY_MESSAGE ;AN000; Display the message + JNC WRITE_MESSAGES_RET ;AN000; Was there an error? + JMP SYSDISP_ERROR ;AN000; YES, display the extended error + +WRITE_MESSAGES_G: ;display adjusted size messages + MOV AX,BUF_SZ ;AN000; "Buffer size: %1 KB",CR,LF + LEA SI,BUF_SZ_SUBLIST ;AN000; Specify SUBLIST to use for replacement + MOV CX,ONE_REPLACE ;AN000; Notify SYSDISPMSG of 1 replacement + CALL DISPLAY_MESSAGE ;AN000; Display the message + JC SYSDISP_ERROR ;AN000; + + MOV AX,SEC_SZ ;AN000; "Sector size: %1",CR,LF + LEA SI,SEC_SZ_SUBLIST ;AN000; Specify SUBLIST to use for replacement + CALL DISPLAY_MESSAGE ;AN000; Display the message + JC SYSDISP_ERROR ;AN000; + + MOV AX,DIR_ENTRIES ;AN000; "Directory entries: %1",CR,LF + LEA SI,DIR_ENT_SUBLIST ;AN000; Specify SUBLIST to use for replacement + CALL DISPLAY_MESSAGE ;AN000; Display the message + JC SYSDISP_ERROR ;AN000; + + CMP CS:EM_SW,0 ;extended memory ? + JE END_LINE ; + MOV AX,TRANS_SZ ;AN000; "Transfer size: %1",CR,LF,CR,LF + LEA SI,TRANS_SZ_SUBLIST ;AN000; Specify SUBLIST to use for replacement + CALL DISPLAY_MESSAGE ;AN000; Display the message + JC SYSDISP_ERROR ;AN000; + + +END_LINE: + RET ;return to INIT_P1 + +SYSDISP_ERROR: + + push ds ;an006; dms;save ds + push bx ;an006; dms;save bx + lds bx,cs:RH_Ptra ;an006; dms;point to request header + mov RH.RH0_Flag,-1 ;an006; dms;signal BIO and error occurred + pop bx ;an006; dms;restore bx + pop ds ;an006; dms;restore ds + + ;AN000; Set error conditions + MOV BX,NO_HANDLE ;AN000; Write to NO_HANDLE + MOV CX,NO_REPLACE ;AN000; + MOV DH,EXT_ERR_CLASS ;AN000; + MOV DL,NO_INPUT ;AN000; + + PUSH DS ;AN000;SET UP ADDRESSIBILITY TO MSG + PUSH ES ;AN000; + + PUSH CS ;AN000;TRANSFER CS + POP DS ;AN000; TO DS + PUSH CX ;AN000;TRANSFER CS + POP ES ;AN000; TO ES + + ASSUME DS:CSEG,ES:CSEG ;AN000; + CALL GET_PARM_SEGMENT ;AN000;OBTAIN PARM SEGMENT + + CALL SYSDISPMSG ;AN000; + + POP ES ;AN000;RESTORE REG + POP DS ;AN000;RESTORE REG + ASSUME DS:NOTHING,ES:NOTHING ;AN000; + + RET ;AN000; + +WRITE_MESSAGES ENDP + + +DISPLAY_MESSAGE PROC NEAR + ;AN000; Set default values + MOV BX,NO_HANDLE ;AN000; Output handle is NO_HANDLE + MOV DH,UTILITY_MSG_CLASS ;AN000; Utility class message + MOV DL,NO_INPUT ;AN000; No input is requested + PUSH DS ;AN000;SET UP ADDRESSIBILITY TO MSG + PUSH ES ;AN000; + + PUSH CS ;AN000;TRANSFER CS + POP DS ;AN000; TO DS + PUSH CS ;AN000;TRANSFER CS + POP ES ;AN000; TO ES + + ASSUME DS:CSEG,ES:CSEG ;AN000; + CALL GET_PARM_SEGMENT ;AN000;OBTAIN PARM SEGMENT + + CALL SYSDISPMSG ;AN000; + + POP ES ;AN000;RESTORE REG + POP DS ;AN000;RESTORE REG + ASSUME DS:NOTHING,ES:NOTHING ;AN000; + + RET ;AN000; + +DISPLAY_MESSAGE ENDP + +GET_PARM_SEGMENT PROC ;AN000;OBTAIN PARM SEGMENT + + PUSH CX ;AN000;SAVE CX - WE STOMP IT + PUSH SI ;AN000;SAVE SI - WE STOMP IT + + CMP CX,00H ;AN000;SEE IF REPLACEMENT IS REQUIRED + JE GPS_END ;AN000;END IF ZERO + + GPS_CONTINUE: ;AN000;LOOP CONTINUE + + MOV [SI].SL_SEGMENT,DS ;AN000;SET UP SEGMENT + ADD SI,11 ;AN000;INCREASE SI BY TABLE SZ + + LOOP GPS_CONTINUE ;AN000;CONTINUE LOOP IF CX NOT ZERO + + GPS_END: ;AN000;EXIT POINT + + POP SI ;AN000;RESTORE SI + POP CX ;AN000;RESTORE CX + + RET ;AN000;RETURN TO CALLER + +GET_PARM_SEGMENT ENDP ;AN000; + + + + +INIT_P1 ENDP ;end of INIT part one +.xlist +MSG_SERVICES ;AN000: +MSG_SERVICES ;AN000; +MSG_SERVICES ;AN000; +MSG_SERVICES ;AN000; +.list +MSGEND LABEL BYTE ;AN000; + +CSEG ENDS + END + \ No newline at end of file diff --git a/v4.0/src/DEV/VDISK/VDISKSYS.INC b/v4.0/src/DEV/VDISK/VDISKSYS.INC new file mode 100644 index 0000000..c9a6c30 --- /dev/null +++ b/v4.0/src/DEV/VDISK/VDISKSYS.INC @@ -0,0 +1,37 @@ +EMS_STATUS EQU 40H +EMS_GET_NUM_PAGES EQU 42H +EMS_ALLOC_PAGES EQU 43H +EMS_MAP_HANDLE EQU 44H +EMS_VERSION EQU 46H +EMS_SAVE_STATE EQU 4F00H +EMS_RESTORE_STATE EQU 4F01H +EMS_SET_HANDLE_NAME EQU 5301H +EMS_FRAME_SEG EQU 5800H +EMS_INT EQU 67H +EMS_VERSION_LEVEL EQU 40H +EMS_2F_Handler EQU 1Bh + +EMS_Mem EQU 'X' ;an005; dms; +EM_Mem EQU 'E' ;an005; dms; + +EMS_EXPECTED_VERSION EQU 34H +EMS_LOW_ERROR EQU 80H +EMS_INSTALLED_FLAG EQU 00h ;ac006; dms; + +EMS_ALLOC_ERROR EQU -1 ; ***RPS ?? +SINGLE_SEGMENT EQU 1 +;;;DOS_PAGE1 EQU 0FFH +;;;DOS_PAGE2 EQU 0FEH +DOS_PAGE_SZ EQU 16 +EMS_Page_Size EQU 4000h ;an002; dms; + +EM_Size_Get EQU 88h ;an001; dms; + + +GET_PAGE_FRAME_STRUC STRUC + FRAME_SEGMENT DW ? + START_PAGE DB ? + NUM_PAGE DB ? +GET_PAGE_FRAME_STRUC ENDS +BUFFER_ENTRY_SIZE EQU TYPE GET_PAGE_FRAME_STRUC + -- cgit v1.2.3