• IBM vdisk.asm (1/3)

    From T. Ment@21:1/5 to All on Tue Jun 23 03:10:55 2020
    IBM provided source code for vdisk 3.3 but omitted some dependencies as mentioned in the .asm file:

    Message text for VDISK is in module VDISKMSG.
    include versiona.inc

    In DOS version 4.0 they bundled everything into vdisk.asm, and now it
    builds with MASM 4.0.

    Hard to find, but PCjs Machines had it. And if that fails, here it is.



    PAGE ,132
    TITLE VDISK - Virtual Disk Device Driver

    ;VDISK simulates a disk drive, using Random Access Memory as the storage medium.

    ;This program is meant to serve as an example of a device driver. It does not ;reflect the current level of VDISK.SYS.

    ;(C) Copyright IBM Corporation, 1984,1985,1988
    ;Licensed Material - Program Property of IBM
    ;Authors: Dick Dievendorff
    ; Rod Springhetti
    ; David M. Sewell

    ;Add the following statement to CONFIG.SYS
    ; DEVICE=[d:][path]VDISK.SYS bbb sss ddd [/E:m]

    ; where: bbb is the desired buffer size (in kilobytes)
    ; minimum 1KB, maximum is size of available memory,
    ; default is 64KB.

    ; VDISK will leave at least 64KB of available memory,
    ; although subsequent device drivers (other than VDISK)
    ; other programs that make themselves resident, and
    ; COMMAND.COM will result in less than 64KB as shown
    ; by CHKDSK.

    ; Must be large enough for 1 boot sector + FAT sectors
    ; + 1 directory sector + at least 1 data cluster,
    ; or the device driver won't be installed.

    ; sss is the desired sector size (in bytes)
    ; 128, 256, or 512, default is 128.
    ; Will be adjusted if number of FAT entries > 0FE0H

    ; ddd is the desired number of directory entries
    ; Minimum 2, maximum 512, default 64.
    ; Will be rounded upward to sector size boundary.

    ; /E may only be used if extended memory above 1 megabyte
    ; is to be used. INT 15H functions 87H and 88H are used
    ; to read and write this extended memory.
    ; The m parameter in the /E option specifies the maximum
    ; number of sectors that the VDISK will transfer at a time.
    ; Optional values are 1,2,3,4,5,6,7 or 8 sectors, the default
    ; is 8 sectors.

    ; Brackets indicate optional operands.


    ; Samples:
    ; DEVICE=\path\VDISK.SYS 160 512 64
    ; results in a 160KB VDISK, with 512 byte sectors, 64 directory entries

    ; DEVICE=VDISK.SYS Buffersize 60 Sectorsize 128 Directory entries 32
    ; (since only numbers are interpreted, you may comment the line with
    ; non-numeric characters)

    SUBTTL Structure Definitions
    PAGE ;-----------------------------------------------------------------------;
    ; Request Header (Common portion) ; ;-----------------------------------------------------------------------;
    RH EQU DS:[BX] ;addressability to Request Header structure

    RHC STRUC ;fields common to all request types
    DB ? ;length of Request Header (including data)
    DB ? ;unit code (subunit)
    RHC_CMD DB ? ;command code
    RHC_STA DW ? ;status
    DQ ? ;reserved for DOS
    RHC ENDS ;end of common portion

    CMD_INPUT EQU 4 ;RHC_CMD is INPUT request

    ;status values for RHC_STA

    STAT_DONE EQU 01H ;function complete status (high order byte) STAT_CMDERR EQU 8003H ;invalid command code error
    STAT_CRC EQU 8004H ;CRC error
    STAT_SNF EQU 8008H ;sector not found error
    STAT_BUSY EQU 0200H ;busy bit (9) for Removable Media call ;-----------------------------------------------------------------------;
    ; Request Header for INIT command ; ;-----------------------------------------------------------------------;
    RH0 STRUC
    DB (TYPE RHC) DUP (?) ;common portion
    RH0_NUN DB ? ;number of units
    ;set to 1 if installation succeeds,
    ;set to 0 to cause installation failure RH0_ENDO DW ? ;offset of ending address
    RH0_ENDS DW ? ;segment of ending address
    RH0_BPBO DW ? ;offset of BPB array address
    RH0_BPBS DW ? ;segment of BPB array address
    RH0_DRIV DB ? ;drive code (DOS 3 only)
    RH0 ENDS

    RH0_BPBA EQU DWORD PTR RH0_BPBO ;offset/segment of BPB array address ;Note: RH0_BPBA at entry to INIT points to all after DEVICE= on CONFIG.SYS stmt

    ;-----------------------------------------------------------------------;
    ; Request Header for MEDIA CHECK Command ; ;-----------------------------------------------------------------------;
    RH1 STRUC
    DB (TYPE RHC) DUP (?) ;common portion
    DB ? ;media descriptor
    RH1_RET DB ? ;return information
    RH1 ENDS ;-----------------------------------------------------------------------;
    ; Request Header for BUILD BPB Command ; ;-----------------------------------------------------------------------;
    RH2 STRUC
    DB (TYPE RHC) DUP(?) ;common portion
    DB ? ;media descriptor
    DW ? ;offset of transfer address
    DW ? ;segment of transfer address
    RH2_BPBO DW ? ;offset of BPB table address
    RH2_BPBS DW ? ;segment of BPB table address
    RH2 ENDS ;-----------------------------------------------------------------------;
    ; Request Header for INPUT, OUTPUT, and OUTPUT with verify ; ;-----------------------------------------------------------------------;
    RH4 STRUC
    DB (TYPE RHC) DUP (?) ;common portion
    DB ? ;media descriptor
    RH4_DTAO DW ? ;offset of transfer address
    RH4_DTAS DW ? ;segment of transfer address
    RH4_CNT DW ? ;sector count
    RH4_SSN DW ? ;starting sector number
    RH4 ENDS

    RH4_DTAA EQU DWORD PTR RH4_DTAO ;offset/segment of transfer address

    ;-----------------------------------------------------------------------;
    ; Segment Descriptor (part of Global Descriptor Table) ; ;-----------------------------------------------------------------------;
    DESC STRUC ;data segment descriptor
    DESC_LMT DW 0 ;segment limit (length)
    DESC_BASEL DW 0 ;bits 15-0 of physical address
    DESC_BASEH DB 0 ;bits 23-16 of physical address
    DB 0 ;access rights byte
    DW 0 ;reserved
    DESC ENDS

    SUBTTL Equates and Macro Definitions
    PAGE

    MEM_SIZE EQU 12H ;BIOS memory size determination INT
    ;returns system size in KB in AX

    EM_INT EQU 15H ;extended memory BIOS interrupt INT
    EM_BLKMOVE EQU 87H ;block move function
    EM_MEMSIZE EQU 88H ;memory size determination in KB

    BOOT_INT EQU 19H ;bootstrap DOS

    DOS EQU 21H ;DOS request INT
    DOS_PCHR EQU 02H ;print character function
    DOS_PSTR EQU 09H ;print string function
    DOS_VERS EQU 30H ;get DOS version

    TAB EQU 09H ;ASCII tab
    LF EQU 0AH ;ASCII line feed
    CR EQU 0DH ;ASCII carriage return
    BEL EQU 07H ;ASCII bell

    PARA_SIZE EQU 16 ;number of bytes in one 8088 paragraph DIR_ENTRY_SIZE EQU 32 ;number of bytes per directory entry
    MAX_FATE EQU 0FE0H ;largest number of FAT entries allowed

    ;default values used if parameters are omitted

    DFLT_BSIZE EQU 64 ;default VDISK buffer size (KB)
    DFLT_SSZ EQU 128 ;default sector size
    DFLT_DIRN EQU 64 ;default number of directory entries
    DFLT_ESS EQU 8 ;default maximum sectors to transfer

    MIN_DIRN EQU 2 ;minimum number of directory entries
    MAX_DIRN EQU 512 ;maximum number of directory entries

    STACK_SIZE EQU 512 ;length of stack during initialization

    ;-----------------------------------------------------------------------;
    ; MSG invokes the console message subroutine ; ;-----------------------------------------------------------------------;

    MSG MACRO TEXT
    PUSH DX ;;save DX across call
    MOV DX,OFFSET TEXT ;;point to message
    CALL SHOW_MSG ;;issue message
    POP DX
    ENDM


    SUBTTL Resident Data Area
    PAGE ;-----------------------------------------------------------------------;
    ; Map INT 19H vector in low storage ; ;-----------------------------------------------------------------------; INT_VEC SEGMENT AT 00H
    ORG 4*BOOT_INT
    BOOT_VEC LABEL DWORD
    BOOT_VECO DW ? ;offset
    BOOT_VECS DW ? ;segment
    INT_VEC ENDS


    CSEG SEGMENT PARA PUBLIC 'CODE'
    ASSUME CS:CSEG ;-----------------------------------------------------------------------;
    ; Resident data area. ;
    ; ;
    ; All variables and constants required after initialization ;
    ; part one are defined here. ; ;-----------------------------------------------------------------------;

    START EQU $ ;begin resident VDISK data & code

    ;DEVICE HEADER - must be at offset zero within device driver
    DD -1 ;becomes pointer to next device header
    DW 0800H ;attribute (IBM format block device)
    ;supports OPEN/CLOSE/RM calls
    DW OFFSET STRATEGY ;pointer to device "strategy" routine
    DW OFFSET IRPT ;pointer to device "interrupt handler"
    DB 1 ;number of block devices
    DB 7 DUP (?) ;7 byte filler (remainder of 8-byte name)
    ;END OF DEVICE HEADER

    ;This volume label is placed into the directory of the new VDISK
    ;This constant is also used to determine if a previous extended memory VDISK ;has been installed.

    VOL_LABEL DB 'VDISK ' ;00-10 volume name (shows program level)
    DB 28H ;11-11 attribute (volume label)
    DT 0 ;12-21 reserved
    DW 6000H ;22-23 time=12:00 noon
    DW 0986H ;24-25 date=12/06/84
    VOL_LABEL_LEN EQU $-VOL_LABEL ;length of volume label

    ;The following field, in the first extended memory VDISK device driver,
    ;is the 24-bit address of the first free byte of extended memory.
    ;This address is not in the common offset/segment format.
    ;The initial value, 10 0000H, is 1 megabyte.

    AVAIL_LO DW 0 ;address of first free byte of
    AVAIL_HI DB 10H ;extended memory

    ;The INT 19H vector is "stolen" by the first VDISK installed in extended memory.
    ;The original content of the interrupt vector is saved here.

    INTV19 LABEL DWORD
    INTV19O DW ? ;offset
    INTV19S DW ? ;segment


    PARAS_PER_SECTOR DW ? ;number of 16-byte paragraphs in one sector

    START_BUFFER_PARA DW ? ;segment address of start of VDISK buffer
    ;for extended memory, this segment address
    ;is the end of the VDISK device driver.

    EM_SW DB 0 ;non-zero if Extended Memory

    EM_STAT DW 0 ;AX from last unsuccessful extended memory I/O

    START_EM_LO DW ? ;24-bit address of start of VDISK buffer START_EM_HI DB ? ;(extended memory only)

    WPARA_SIZE DW PARA_SIZE ;number of bytes in one paragraph

    MAX_CNT DW ? ;(0FFFFH/BPB_SSZ) truncated, the maximum
    ;number of sectors that can be transferred
    ;without worrying about 64KB wrap

    SECT_LEFT DW ? ;sectors left to transfer

    IO_SRCA LABEL DWORD ;offset/segment of source
    IO_SRCO DW ? ;offset
    IO_SRCS DW ? ;segment

    IO_TGTA LABEL DWORD ;offset/segment of target
    IO_TGTO DW ? ;offset
    IO_TGTS DW ? ;segment

    ;-----------------------------------------------------------------------;
    ; BIOS Parameter Block (BPB) ; ;-----------------------------------------------------------------------;
    ;This is where the characteristics of the virtual disk are established.
    ;A copy of this block is moved into the boot record of the virtual disk.
    ;DEBUG can be used to read sector zero of the virtual disk to examine the
    ;boot record copy of this block.

    BPB LABEL BYTE ;BIOS Parameter Block (BPB)
    BPB_SSZ DW 0 ;number of bytes per disk sector
    BPB_AUSZ DB 1 ;sectors per allocation unit
    BPB_RES DW 1 ;number of reserved sectors (for boot record) BPB_FATN DB 1 ;number of File Allocation Table (FAT) copies BPB_DIRN DW 0 ;number of root directory entries
    BPB_SECN DW 1 ;total number of sectors
    ;computed from buffer size and sector size
    ;(this includes reserved, FAT, directory,
    ;and data sectors)
    BPB_MCB DB 0FEH ;media descriptor byte
    BPB_FATSZ DW 1 ;number of sectors occupied by a single FAT
    ;computed from BPBSSZ and BPBSECN
    BPB_LEN EQU $-BPB ;length of BIOS parameter block

    BPB_PTR DW BPB ;BIOS Parameter Block pointer array (1 entry) ;-----------------------------------------------------------------------;
    ; Request Header (RH) address, saved here by "strategy" routine ; ;-----------------------------------------------------------------------; RH_PTRA LABEL DWORD
    RH_PTRO DW ? ;offset
    RH_PTRS DW ? ;segment ;-----------------------------------------------------------------------;
    ; Global Descriptor Table (GDT), used for extended memory moves ; ;-----------------------------------------------------------------------; ;Access Rights Byte (93H) is
    ; P=1 (segment is mapped into physical memory)
    ; E=0 (data segment descriptor)
    ; D=0 (grow up segment, offsets must be <= limit)
    ; W=1 (data segment may be written into)
    ; DPL=0 (privilege level 0)

    GDT LABEL BYTE ;begin global descriptor table
    DESC <> ;dummy descriptor
    DESC <> ;descriptor for GDT itself
    SRC DESC <,,,93H,> ;source descriptor
    TGT DESC <,,,93H,> ;target descriptor
    DESC <> ;BIOS CS descriptor
    DESC <> ;stack segment descriptor

    SUBTTL INT 19H (boot) interrupt handler
    PAGE ;-----------------------------------------------------------------------;
    ; INT 19H Interrupt Handler routine ; ;-----------------------------------------------------------------------;
    ;The INT 19H vector is altered by VDISK initialization to point to this ;routine within the first extended memory VDISK device driver.

    ;The vector points to the device driver so that subsequent VDISKs installed
    ;in extended memory can find the first one to determine what memory has ;already been allocated to VDISKs.

    ;This routine restores the original INT 19H vector's content, then jumps
    ;to the original routine.

    ;INT 19H, the "Boot" INT, is always altered when DOS is booted.

    ;This routine is entered with interrupts disabled.

    VDISK_INT19 PROC ;INT 19H received
    PUSH DS ;save registers we're going to alter
    PUSH AX

    XOR AX,AX
    MOV DS,AX ;set DS = 0
    ASSUME DS:INT_VEC

    MOV AX,CS:INTV19O ;get offset of saved vector
    MOV DS:BOOT_VECO,AX ;store offset in interrupt vector

    MOV AX,CS:INTV19S ;get segment of saved vector
    MOV DS:BOOT_VECS,AX ;store segment in interrupt vector

    POP AX
    POP DS

    JMP CS:INTV19 ;go to original interrupt routine

    VDISK_INT19 ENDP

    ASSUME DS:NOTHING

    SUBTTL Device Strategy & interrupt entry points
    PAGE ;-----------------------------------------------------------------------;
    ; Device "strategy" entry point ;
    ; ;
    ; Retain the Request Header address for use by Interrupt routine ; ;-----------------------------------------------------------------------; STRATEGY PROC FAR
    MOV CS:RH_PTRO,BX ;offset
    MOV CS:RH_PTRS,ES ;segment
    RET
    STRATEGY ENDP ;-----------------------------------------------------------------------;
    ; Table of command processing routine entry points ; ;-----------------------------------------------------------------------; CMD_TABLE LABEL WORD
    DW OFFSET INIT_P1 ; 0 - Initialization
    DW OFFSET MEDIA_CHECK ; 1 - Media check
    DW OFFSET BLD_BPB ; 2 - Build BPB
    DW OFFSET INPUT_IOCTL ; 3 - IOCTL input
    DW OFFSET INPUT ; 4 - Input
    DW OFFSET INPUT_NOWAIT ; 5 - Non destructive input no wait
    DW OFFSET INPUT_STATUS ; 6 - Input status
    DW OFFSET INPUT_FLUSH ; 7 - Input flush
    DW OFFSET OUTPUT ; 8 - Output
    DW OFFSET OUTPUT_VERIFY ; 9 - Output with verify
    DW OFFSET OUTPUT_STATUS ;10 - Output status
    DW OFFSET OUTPUT_FLUSH ;11 - Output flush
    DW OFFSET OUTPUT_IOCTL ;12 - IOCTL output
    DW OFFSET DEVICE_OPEN ;13 - Device OPEN
    DW OFFSET DEVICE_CLOSE ;14 - Device CLOSE
    MAX_CMD EQU ($-CMD_TABLE)/2 ;highest valid command follows
    DW OFFSET REMOVABLE_MEDIA ;15 - Removable media

    ;-----------------------------------------------------------------------;
    ; Device "interrupt" entry point ; ;-----------------------------------------------------------------------;
    IRPT PROC FAR ;device interrupt entry point
    PUSH DS ;save all registers modified
    PUSH ES
    PUSH AX
    PUSH BX
    PUSH CX
    PUSH DX
    PUSH DI
    PUSH SI
    ;BP isn't used, so it isn't saved
    CLD ;all moves forward

    LDS BX,CS:RH_PTRA ;get RH address passed to "strategy" into DS:BX

    MOV AL,RH.RHC_CMD ;command code from Request Header
    CBW ;zero AH (if AL > 7FH, next compare will
    ;catch that error)

    CMP AL,MAX_CMD ;if command code is too high
    JA IRPT_CMD_HIGH ;jump to error routine

    MOV DI,OFFSET IRPT_CMD_EXIT ;return addr from command processor
    PUSH DI ;push return address onto stack
    ;command routine issues "RET"

    ADD AX,AX ;double command code for table offset
    MOV DI,AX ;put into index register for JMP

    XOR AX,AX ;initialize return to "no error"

    ;At entry to command processing routine:

    ; DS:BX = Request Header address
    ; CS = VDISK code segment address
    ; AX = 0

    ; top of stack is return address, IRPT_CMD_EXIT

    JMP CS:CMD_TABLE[DI] ;call routine to handle the command


    IRPT_CMD_ERROR: ;CALLed for unsupported character mode commands

    INPUT_IOCTL: ;IOCTL input
    INPUT_NOWAIT: ;Non-destructive input no wait
    INPUT_STATUS: ;Input status
    INPUT_FLUSH: ;Input flush

    OUTPUT_IOCTL: ;IOCTL output
    OUTPUT_STATUS: ;Output status
    OUTPUT_FLUSH: ;Output flush

    POP AX ;pop return address off stack

    IRPT_CMD_HIGH: ;JMPed to if RHC_CMD > MAX_CMD
    MOV AX,STAT_CMDERR ;"invalid command" and error

    IRPT_CMD_EXIT: ;return from command routine
    ;AX = value to OR into status word
    LDS BX,CS:RH_PTRA ;restore DS:BX as Request Header pointer
    OR AH,STAT_DONE ;add "done" bit to status word
    MOV RH.RHC_STA,AX ;store status into request header
    POP SI ;restore registers
    POP DI
    POP DX
    POP CX
    POP BX
    POP AX
    POP ES
    POP DS
    RET
    IRPT ENDP

    SUBTTL Command Processing routines
    PAGE ;-----------------------------------------------------------------------;
    ; Command Code 1 - Media Check ;
    ; At entry, DS:BX point to request header, AX = 0 ; ;-----------------------------------------------------------------------; MEDIA_CHECK PROC
    MOV RH.RH1_RET,1 ;indicate media not changed
    RET ;AX = zero, no error
    MEDIA_CHECK ENDP ;-----------------------------------------------------------------------;
    ; Command Code 2 - Build BPB ;
    ; At entry, DS:BX point to request header, AX = 0 ; ;-----------------------------------------------------------------------; BLD_BPB PROC
    MOV RH.RH2_BPBO,OFFSET BPB ;return pointer to our BPB
    MOV RH.RH2_BPBS,CS
    RET ;AX = zero, no error
    BLD_BPB ENDP ;-----------------------------------------------------------------------;
    ; Command Code 13 - Device Open ;
    ; Command Code 14 - Device Close ;
    ; Command Code 15 - Removable media ;
    ; At entry, DS:BX point to request header, AX = 0 ; ;-----------------------------------------------------------------------; REMOVABLE_MEDIA PROC
    MOV AX,STAT_BUSY ;set status bit 9 (busy)
    ;indicating non-removable media DEVICE_OPEN: ;NOP for device open
    DEVICE_CLOSE: ;NOP for device close
    RET
    REMOVABLE_MEDIA ENDP ;fall thru to return ;-----------------------------------------------------------------------;
    ; Command Code 4 - Input ;
    ; Command Code 8 - Output ;
    ; Command Code 9 - Output with verify ;
    ; At entry, DS:BX point to request header, AX = 0 ; ;-----------------------------------------------------------------------;
    INOUT PROC
    INPUT:
    OUTPUT:
    OUTPUT_VERIFY:

    ;Make sure I/O is entirely within the VDISK sector boundaries

    MOV CX,CS:BPB_SECN ;get total sector count
    MOV AX,RH.RH4_SSN ;starting sector number
    CMP AX,CX ;can't exceed total count
    JA INOUT_E1 ;jump if start > total

    ADD AX,RH.RH4_CNT ;start + sector count
    CMP AX,CX ;can't exceed total count
    JNA INOUT_A ;jump if start + count <= total

    INOUT_E1: ;I/O not within VDISK sector boundaries
    MOV RH.RH4_CNT,0 ;set sectors transferred to zero
    MOV AX,STAT_SNF ;indicate 'Sector not found' error
    RET ;return with error status in AX

    INOUT_A: ;I/O within VDISK bounds
    MOV AX,RH.RH4_CNT ;get sector count
    MOV CS:SECT_LEFT,AX ;save as sectors left to process

    CMP CS:EM_SW,0 ;extended memory mode?
    JNE INOUT_EM ;jump to extended memory I/O code

    ;Compute offset and segment of VDISK buffer for starting segment in CX:SI

    MOV AX,RH.RH4_SSN ;starting sector number
    MUL CS:PARAS_PER_SECTOR ;* length of one sector in paragraphs
    ADD AX,CS:START_BUFFER_PARA ;+ segment of VDISK buffer sector 0
    MOV CX,AX ;segment address to CX
    XOR SI,SI ;offset is zero

    ;Compute address of caller's Data Transfer Addr in DX:AX with smallest offset, ;so that there is no possibility of overflowing a 64KB boundary moving MAX_CNT ;sectors.

    MOV AX,PARA_SIZE ;16
    MUL RH.RH4_DTAS ;* segment of caller's DTA in DX,AX
    ADD AX,RH.RH4_DTAO ;+ offset of caller's DTA
    ADC DL,0 ;carry in from addition
    DIV CS:WPARA_SIZE ;AX is segment of caller's DTA
    ;DX is smallest offset possible
    ;AX:DX = DTA address

    ;AX:DX is caller's DTA segment:offset, CX:SI is VDISK buffer segment:offset

    ;If this is an OUTPUT request, exchange the source and target addresses

    CMP RH.RHC_CMD,CMD_INPUT ;INPUT operation?
    JE INOUT_B ;jump if INPUT operation

    XCHG AX,CX ;swap source and target segment
    XCHG DX,SI ;swap source and target offset

    INOUT_B: ;CX:SI is source, AX:DX is target
    MOV CS:IO_SRCS,CX ;save source segment
    MOV CS:IO_SRCO,SI ;save source offset
    MOV CS:IO_TGTS,AX ;save target segment
    MOV CS:IO_TGTO,DX ;save target offset

    JMP SHORT INOUT_E ;AX := SECT_LEFT, test for zero INOUT_C: ;SECT_LEFT in AX, non-zero

    ; Compute number of sectors to transfer in a single move,
    ; AX = minimum of (SECT_LEFT, MAX_CNT)

    ; MAX_CNT is the maximum number of sectors that can be moved without
    ; spanning a 64KB boundary (0FFFFH / Sector size, remainder truncated)

    MOV CX,CS:MAX_CNT ;MAX sectors with one move
    CMP AX,CX ;if SECT_LEFT cannot span 64KB boundary
    JBE INOUT_D ;then move SECT_LEFT sectors

    MOV AX,CX ;else move MAX_CNT sectors
    INOUT_D:
    SUB CS:SECT_LEFT,AX ;reduce number of sectors left to move

    ;Move AX sectors from source to target

    MUL CS:BPB_SSZ ;sectors * sector size = byte count
    ;(cannot overflow into DX)
    SHR AX,1 ;/2 = word count
    MOV CX,AX ;word count to CX for REP MOVSW

    LDS SI,CS:IO_SRCA ;source segment/offset to DS:SI
    LES DI,CS:IO_TGTA ;target segment/offset to ES:DI

    REP MOVSW ;move MOV_CNT sectors

    ;Update source and target paragraph addresses
    ;AX has number of words moved

    SHR AX,1 ;words moved / 8 = paragraphs moved
    SHR AX,1
    SHR AX,1

    ADD CS:IO_SRCS,AX ;add paragraphs moved to source segment
    ADD CS:IO_TGTS,AX ;add paragraphs moved to target segment

    ;Determine if more sectors need to be transferred

    INOUT_E: ;do while SECT_LEFT <> zero
    MOV AX,CS:SECT_LEFT ;get sectors left to transfer
    OR AX,AX ;set flags
    JNZ INOUT_C ;go back to transfer some sectors
    RET ;AX = zero, all sectors transferred

    SUBTTL Extended Memory I/O routine
    PAGE ;-----------------------------------------------------------------------;
    ; Extended Memory I/O routine ; ;-----------------------------------------------------------------------; INOUT_EM: ;Extended memory I/O routine
    ;change to larger stack
    MOV SI,SS ;save old SS in SI
    MOV DX,SP ;save old SP in DX
    CLI ;disable interrupts
    MOV AX,CS
    MOV SS,AX ;set SS = CS
    MOV SP,OFFSET EM_STACK ;point to new stack
    STI ;enable interrupts
    PUSH SI ;save old SS at top of new stack
    PUSH DX ;save old SP on new stack

    MOV SI,RH.RH4_DTAO ;caller's DTA offset

    ;Compute 24-bit address of VDISK sector in CX (hi) and SI (low)

    MOV AX,RH.RH4_SSN ;starting sector number
    MUL CS:BPB_SSZ ;* sector size = offset within buffer
    ADD AX,CS:START_EM_LO ;+ base address of this VDISK buffer
    ADC DL,CS:START_EM_HI
    MOV CX,DX ;save high byte
    MOV SI,AX ;save low word

    ;Compute 24-bit address of caller's DTA in DX (hi) and AX (low)

    MOV AX,PARA_SIZE ;16
    MUL RH.RH4_DTAS ;* segment of caller's DTA
    ADD AX,RH.RH4_DTAO ;+ offset of caller's DTA
    ADC DL,0 ;carry in from addition

    ;Caller's DTA address is in CX,SI, VDISK buffer address is in DX,AX.

    ;If this is an OUTPUT request, exchange the source and target addresses

    CMP RH.RHC_CMD,CMD_INPUT ;INPUT operation?
    JE INOUT_EM_B ;jump if INPUT operation

    XCHG DX,CX ;swap source and target high byte
    XCHG AX,SI ;swap source and target low word

    INOUT_EM_B: ;CX,SI is source, DX,AX is target

    MOV SRC.DESC_BASEL,SI ;low 16 bits of source address
    MOV SRC.DESC_BASEH,CL ;high 8 bits of source address

    MOV TGT.DESC_BASEL,AX ;low 16 bits of target address
    MOV TGT.DESC_BASEH,DL ;high 8 bits of target address

    JMP SHORT INOUT_EM_E ;AX := SECT_LEFT, test for zero INOUT_EM_C: ;SECT_LEFT in AX, non-zero

    ; Compute number of sectors to transfer in a single move,
    ; AX = minimum of (SECT_LEFT, MAX_CNT)

    ; MAX_CNT is the maximum number of sectors that can be moved without
    ; spanning a 64KB boundary (0FFFFH / Sector size, remainder truncated)

    MOV CX,CS:MAX_CNT ;MAX sectors with one move
    CMP AX,CX ;if SECT_LEFT cannot span 64KB boundary
    JBE INOUT_EM_D ;then move SECT_LEFT sectors

    MOV AX,CX ;else move MAX_CNT sectors
    INOUT_EM_D:
    SUB CS:SECT_LEFT,AX ;reduce number of sectors left to move

    ;Move AX sectors from source to target

    MUL CS:BPB_SSZ ;sectors * sector size = byte count
    ;(cannot overflow into DX)
    MOV TGT.DESC_LMT,AX ;store segment limit (byte count)
    MOV SRC.DESC_LMT,AX

    PUSH AX ;preserve byte count on stack

    SHR AX,1 ;/2 = word count
    MOV CX,AX ;word count to CX

    PUSH CS
    POP ES ;set ES = CS
    MOV SI,OFFSET GDT ;ES:SI point to GDT

    MOV AH,EM_BLKMOVE ;function is block move
    INT EM_INT ;move an even number of words

    POP CX ;get byte count back from stack

    OR AH,AH ;get error code

    JNZ INOUT_EM_XE ;jump if I/O error encountered

    ;Update source and target addresses

    ADD SRC.DESC_BASEL,CX ;add bytes moved to source
    ADC SRC.DESC_BASEH,0 ;pick up any carry

    ADD TGT.DESC_BASEL,CX ;add bytes moved to target
    ADC TGT.DESC_BASEH,0 ;pick up any carry

    ;Determine if more sectors need to be transferred

    INOUT_EM_E: ;do while SECT_LEFT <> zero
    MOV AX,CS:SECT_LEFT ;get sectors left to transfer
    OR AX,AX ;set flags
    JNZ INOUT_EM_C ;go back to transfer some sectors

    INOUT_EM_X2: ;revert to original stack
    POP DI ;get old SP
    POP SI ;get old SS
    CLI ;disable interrupts
    MOV SS,SI ;restore old SS
    MOV SP,DI ;restore old SP
    STI ;enable interrupts
    RET ;return to IRPT_EXIT

    INOUT_EM_XE: ;some error with INT 15H
    MOV CS:EM_STAT,AX ;save error status for debugging
    MOV RH.RH4_CNT,0 ;indicate no sectors transferred
    MOV AX,STAT_CRC ;indicate CRC error
    JMP INOUT_EM_X2 ;fix stack and exit
    INOUT ENDP

    DW 40 DUP (?) ;stack for extended memory I/O
    EM_STACK LABEL WORD

    SUBTTL Boot Record
    PAGE ;-----------------------------------------------------------------------;
    ; Adjust the assembly-time instruction counter to a paragraph ;
    ; boundary ; ;-----------------------------------------------------------------------;

    IF ($-START) MOD 16
    ORG ($-START) + 16 - (($-START) MOD 16)
    ENDIF

    VDISK EQU $ ;start of virtual disk buffer
    VDISKP EQU ($-START) / PARA_SIZE ;length of program in paragraphs ;-----------------------------------------------------------------------;
    ; If this VDISK is in extended memory, this address is passed ;
    ; back to DOS as the end address that is to remain resident. ;
    ; ;
    ; It this VDISK is not in extended memory, the VDISK buffer ;
    ; begins at this address, and the address passed back to DOS ;
    ; as the end address that is to remain resident is this address ;
    ; plus the length of the VDISK buffer. ; ;-----------------------------------------------------------------------;

    BOOT_RECORD LABEL BYTE ;Format of Boot Record documented in
    ;DOS Technical Reference Manual
    DB 0,0,0 ;3-byte jump to boot code (not bootable)
    DB 'VDISK ' ;8-byte vendor identification
    BOOT_BPB LABEL BYTE ;boot record copy of BIOS parameter block
    DW ? ;number of bytes per disk sector
    DB ? ;sectors per allocation unit
    DW ? ;number of reserved sectors (for boot record)
    DB ? ;number of File Allocation Table (FAT) copies
    DW ? ;number of root directory entries
    DW ? ;total number of sectors
    DB ? ;media descriptor byte
    DW ? ;number of sectors occupied by a single FAT ;end of boot record BIOS Parameter block

    ;The following three words mean nothing to VDISK, they are placed here
    ;to conform to the DOS standard for boot records.
    DW 8 ;sectors per track
    DW 1 ;number of heads
    DW 0 ;number of hidden sectors
    ;The following word is the 16-bit kilobyte address of the first byte in ;extended memory that is not occupied by a VDISK buffer
    ;It is placed into this location so that other users of extended memory
    ;may find where all the VDISKs end.

    ;This field may be accessed by moving the boot record of the First extended ;memory VDISK from absolute location 10 0000H. Before assuming that the
    ;value below is valid, the vendor ID (constant VDISK) should be verified
    ;to make sure that SOME VDISK has been installed.

    ;For example, if two VDISKs are installed, one 320KB and one 64KB, the
    ;address calculations are as follows:

    ;Extended memory start address = 100000H (1024KB)
    ;Start addr of 1st VDISK buffer = 100000H (1024KB)
    ;Length of 1st VDISK buffer = 050000H ( 320KB)
    ;End addr of 1st VDISK buffer = 14FFFFH
    ;Start addr of 2nd VDISK buffer = 150000H (1344KB)
    ;Length of 2nd VDISK buffer = 010000H ( 64KB)
    ;End addr of 2nd VDISK buffer = 15FFFFH
    ;First byte after all VDISKs = 160000H (1408KB)
    ;Divide by 1024 = 0580H (1408D)

    ;Content of BOOT_EM = 0580H

    BOOT_EM_OFF EQU $-BOOT_RECORD ;offset from 10 0000H of the following word BOOT_EM DW 1024 ;KB addr of first free byte of extended memory ;-----------------------------------------------------------------------;
    ; Part 2 of Initialization (executed last) ; ;-----------------------------------------------------------------------; ;Initialization is divided into two parts.

    ;INIT_P1 is overlaid by the virtual disk buffer

    ;INIT_P1 is executed first, then jumps to INIT_P2. INIT_P2 returns to caller.

    ;Exercise caution if extending the initialization part 2 code.
    ;It overlays the area immediately following the boot sector.
    ;If this section of code must be expanded, make sure it fits into the minimum ;sector size of 128 bytes.
    ;Label TEST_LENGTH must equate to a non-negative value (TEST_LENGTH >= 0).
    ;If this code it must be extended beyond the 128 byte length of the boot sector,
    ;move all of INIT_P2 before label VDISK.

    ;Registers at entry to INIT_P2 (set up at end of INIT_P1):
    ; BL = media control byte from BPB (for FAT)
    ; CX = number of FAT copies
    ; DX = number of bytes in one FAT - 3
    ; SI = OFFSET of Volume Label field
    ; ES:DI = VDISK buffer address of first FAT sector
    ; CS = DS = VDISK code segment

    INIT_P2 PROC ;second part of initialization
    ASSUME DS:CSEG ;DS set in INIT_P1

    ;Initialize File Allocation Table(s) (FATs)

    INIT_P2_FAT: ;set up one FAT, sector number in AX

    PUSH CX ;save loop counter on stack
    MOV AL,BL ;media control byte
    STOSB ;store media control byte, increment DI
    MOV AX,0FFFFH ;bytes 2 and 3 of FAT are 0FFH
    STOSW

    MOV CX,DX ;FAT size in bytes - 3
    XOR AX,AX ;value to store in remainder of FAT
    REP STOSB ;clear remainder of FAT

    POP CX ;get loop counter off stack
    LOOP INIT_P2_FAT ;loop for all copies of the FAT

    ;Put the volume label in the first directory entry

    MOV CX,VOL_LABEL_LEN ;length of volume directory entry
    REP MOVSB ;move volume id to directory

    ;Zero the remainder of the directory

    MOV AX,DIR_ENTRY_SIZE ;length of 1 directory entry
    MUL BPB_DIRN ;* number entries = bytes of directory
    SUB AX,VOL_LABEL_LEN ;less length of volume label
    MOV CX,AX ;length of rest of directory
    XOR AX,AX
    REP STOSB ;clear directory to nulls
    RET ;return with AX=0
    INIT_P2 ENDP

    PATCH_AREA DB 5 DUP ('PATCH AREA ')
    TEST_LENGTH EQU 128-($-VDISK) ;if negative, boot record has too much
    ;data area, move some fields below VDISK
    ;-----------------------------------------------------------------------;
    ; All fields that must remain resident after device driver ;
    ; initialization must be defined before this point. ; ;-----------------------------------------------------------------------;
    DB 'IBM DOS Version 4.00, Virtual Disk Device Driver'
    DB '(C)Copyright IBM Corp 1984,1985,1988'

    [continued in next message]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From T. Ment@21:1/5 to T. Ment on Tue Jun 23 18:34:27 2020
    On Tue, 23 Jun 2020 03:10:55 +0000, T. Ment wrote:

    In DOS version 4.0 they bundled everything into vdisk.asm, and now it
    builds with MASM 4.0.

    Uh-oh crashed with a divide overflow.

    I loaded SoftICE to debug but then it didn't crash. Hmmm. SoftICE limits
    memory to 16 MB, maybe that's why.

    Then, instead of SoftICE, I tried TDG16M.SYS which also limits memory to
    16 MB. That worked too. So VDISK can't handle > 16MB memory. Needs a bug
    fix.

    Files like TDG16M.SYS (from Borland) are hard to find now. Any ideas for
    a public share location?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From JJ@21:1/5 to T. Ment on Wed Jun 24 20:53:37 2020
    On Tue, 23 Jun 2020 18:34:27 +0000, T. Ment wrote:

    Uh-oh crashed with a divide overflow.

    I loaded SoftICE to debug but then it didn't crash. Hmmm. SoftICE limits memory to 16 MB, maybe that's why.

    Then, instead of SoftICE, I tried TDG16M.SYS which also limits memory to
    16 MB. That worked too. So VDISK can't handle > 16MB memory. Needs a bug
    fix.

    Files like TDG16M.SYS (from Borland) are hard to find now. Any ideas for
    a public share location?

    Found a mirror.

    ftp://69.43.38.172/mirrors/cd.textfiles.com/swextrav4-3/progtool/tdg16m.zip

    From Mamont FTP search.

    https://www.mmnt.ru/int/get?st=TDG16M

    And apparently, the source server is still online.

    http://cd.textfiles.com/swextrav4-3/progtool/tdg16m.zip

    That driver simply hook Int 15h AH=88h and return the wanted extended memory capacity, if the specified size is smaller than the actual capacity.
    Otherwise, the drive won't load because it's not needed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From T. Ment@21:1/5 to All on Wed Jun 24 14:56:18 2020
    On Wed, 24 Jun 2020 20:53:37 +0700, JJ wrote:

    Files like TDG16M.SYS (from Borland) are hard to find now. Any ideas for
    a public share location?

    Mamont FTP search.
    https://www.mmnt.ru/int/get?st=TDG16M

    I didn't know about that one. Filewatcher was good but it's long gone.

    I have a free mega.nz account where I could save old files. Never tried
    it though, so I don't know if it's easy to use or not.

    Any other ideas for file sharing?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From JJ@21:1/5 to T. Ment on Fri Jun 26 03:31:32 2020
    On Wed, 24 Jun 2020 14:56:18 +0000, T. Ment wrote:
    On Wed, 24 Jun 2020 20:53:37 +0700, JJ wrote:

    Files like TDG16M.SYS (from Borland) are hard to find now. Any ideas for >>> a public share location?

    Mamont FTP search.
    https://www.mmnt.ru/int/get?st=TDG16M

    I didn't know about that one. Filewatcher was good but it's long gone.

    I have a free mega.nz account where I could save old files. Never tried
    it though, so I don't know if it's easy to use or not.

    Any other ideas for file sharing?

    Oh, my bad. I thought you were looking for that TDG16M.SYS.

    IMO, Mega is not low-end-PC friendly due to its high memory usage.

    Other good alternatives (in general) are MediaFire, Dropbox, Google Drive, Microsoft One Drive, Degoo.

    Google Drive is best in terms of maximum file size, and overall best for the combination of storage capacity and upload retention.

    Degoo is best in terms of storage capacity (100GB last time I check), but
    its 42 days account activity is a big disadvantage.

    All these requires user registration, BTW.

    For file sharing without registration requirement, I have these in my
    bookmark.

    fileconvoy.com
    gofile.io
    uguu.se
    workupload.com
    zippyshare.com

    Keep in mind that almost all file sharing without registration requirement, have low upload retention.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)