TITLE EDISK - INSTALLABLE DEVICE DRIVER FOR A HIGH MEMORY VIRTUAL DISK ;11/10/85 ; This ram disk uses the 128K portion of memory above 640K be- ; ginning at segment D000:0000 and ending at F000:0000. ; The original code comes from the DOS 2.0 Technical Manual, later ; modified by Rex McAnally to allow variable sizes. (I have ; included his original notes below.) ; The code has been modified in accordence with the POWER USER ; column in PC Magazine, Oct. 15, 1985. It includes the following ; features: ; a. It will assemble with both versions 1 & 2 MACRO ; Assenbler ; ; b. The program initializes the 128K RAM to prevent the ; dreaded PARITY error (DOS won't do it) ; ; c. The contents of the Ram disk will survive a warm boot ; (Ctl-Alt-Del) ; ; You have to provide the 128K ram memory addressable at segment D000 ; Note: This 128K was reserved for ROM based programs like those found ; for the PC jr. None are available for the PC/PC-XT so the ; memory space is normally unusable & wasted. It can't be added ; to DOS as it is'nt continuous with the 640K user's area. ; ; The easiest way to add this memory to your PC is to buy an IBM ; 64/256K Memory Expansion Option board, install 128K on it, and ; set its DIP switches for a start address of D000 like so: ; ; switch 1 2 3 4 5 6 7 8 ; position off off on off off on off off ; ; Not all memory or combo memory boards will do. Most don't allow ; you to choose the absolute starting segment address. ; ; To use this program: ; ; 1. Assemble this program using the MACRO Assembler ; ; 2. Link the resultant .OBJ file using LINK (DOS utility) ; ; 3. Convert the resultant .EXE file to binary form using ; EXE2BIN (also a DOS utility), and name it EDISK.SYS ; ; 4. Copy the EDISK.SYS file to your BOOT/Root directory ; ; 5. Add DEVICE=EDISK.SYS as the **FIRST** line of your ; CONFIG.SYS file. It M-u-s-t be first as the program ; has to know whether it sees a first or a subsequent ; Boot. Some Devive drivers cause a double Boot and ; if they precede EDISK.SYS they will cause EDISK to ; think an initial cold Boot is a warm Boot. This ; screws up the Ram Disk data preserve function and ; will most likely cause your system to hang up. ; ; A driver that adds 64K above the 640K user area to DOS (IF you have ; Ram there), such as MEMM.SYS works OK as long as it follows the EDISK ; driver in CONFIG.SYS. ; ; I have marked the lines I modified with EM and those I added ; with EM-A. By now this code has been pretty well hacked and ; could stand some cleaning up, but it works fine as is. Feel free ; to clean it up or improve it further. ; ; ENJOY, ; Bill Wacker ; ; ;Rex McNally's unmodified notes follow: ; ; This was originally the virtual disk device contained in ; the DOS 2.0 manual (listing only). ; ; The code has been modified to allow a ram disk of more than ; 180k. This can be specified in 2 ways. ; (1). Changing the RAMDISK equate statment to the desired size in ; 1 K increments, reassembling, linking, using EXE2BIN, ; to convert to "com" file format. (see DOS 2.0 manual on ; device drivers. ; (2). Rename the "VDISK.COM" file to "VDISK.XXX", ; Using DEBUG you can modify the size by changing offsets ; 29-30 hex in the file. offset 29 = low order byte, 30 = h/o ; These 2 bytes contain the size in 1K increments. ; ; The current maximum limit is 512K (0200 hex). ; ; All modified lines of code have been labeled with comments. ; Please feel free to play with this, I am considering enhancing it ; to ask the operator for a size before completing the device ; installation. ; Rex McAnally ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Minor touch-up mods made by L. Shannon 3/3/89 ; ; NOTE: Disregard most of the above info. ; This program must NOT be used with parity-checked ram because ; it attempts to read it before writing to it. ; ; This will cause a system crash. ; ; Use only with static ram. ; ; If not backed up by battery, it will reinitialize at power up ; Will almost surely preserve the ramdisk through a reset, though. ; PAGE CSEG SEGMENT PARA PUBLIC 'CODE' ; ;*** MACRO DEFINITIONS ; STATUS MACRO STATE,ERR,RC IFIDN , OR ES:WORD PTR SRH_STA_FLD[BX],0100H ENDIF IFIDN , OR ES:WORD PTR SRH_STA_FLD[BX],0200H ENDIF IFIDN , OR ES:WORD PTR SRH_STA_FLD[BX],1000H ENDIF IFNB OR ES:WORD PTR SRH_STA_FLD[BX],RC ENDIF ENDM ; ;***Equates ; ;***Size ; SECT EQU 512 ; size of a sector RAMDISK EQU 128 ; size of ramdisk in 1k increments FATSIZE EQU 1 ; # sect in 1 copy of fat ; ;***Read/Write ; SRH EQU 0 ;Static request header start SRH_LEN EQU 13 ; " " " length SRH_LEN_FLD EQU SRH ; " " " length field SRH_UCD_FLD EQU SRH+1 ; " " " unit code field SRH_CCD_FLD EQU SRH+2 ; " " " command code fld SRH_STA_FLD EQU SRH+3 ; " " " status field SRH_RES_FLD EQU SRH+5 ; " " " reserved area fld MD EQU SRH+SRH_LEN ;Media descriptor byte MD_LEN EQU 1 ; " " " length DTA EQU MD+MD_LEN ;Disk transfer address DTA_LEN EQU 4 ;DTA length COUNT EQU DTA+DTA_LEN ;Byte/Sector count COUNT_LEN EQU 2 ; " " length SSN EQU COUNT+COUNT_LEN ;Starting sector number SSN_LEN EQU 2 ; " " " length ; ;***Media check ; RET_BYTE EQU MD+MD_LEN ;Byte returned from driver ; ;***Build BPB ; BPBA_PTR EQU DTA+DTA_LEN BPBA_PTA_LEN EQU 4 ; ;***Init ; UNITS EQU SRH+SRH_LEN UNITS_LEN EQU 1 BR_ADDR_0 EQU UNITS+UNITS_LEN BR_ADDR_1 EQU BR_ADDR_0+2 BR_ADDR_LEN EQU 4 BPB_PTR_OFF EQU BR_ADDR_0+BR_ADDR_LEN BPB_PTR_SEG EQU BPB_PTR_OFF+2 ; ; VDSK PROC FAR ASSUME CS:CSEG,ES:CSEG,DS:CSEG BEGIN: START EQU $ ; ;***Special device header ; NEXT_DEV DD -1 ;Pointer to next device ATTRIBUTE DW 2000H ;Block device (NON-IBM FORMAT) STATEGY DW DEV_STATEGY ;Pointer to device stategy INTERRUPT DW DEV_INT ;Pointer to device interrupt handler DEV_NAME DB 1 ;Number of block devices DB 7 DUP(?) ;7 bytes of filler RH_OFF DW ? ;Request header offset RH_SEG DW ? ;Request header segment ; ;***BIOS parameter block ; BPB EQU $ DW SECT ;Sector size DB 1 ;Sectors/allocation unit DW 1 ;Number of reserved sectors DB 2 ;Number of FATS DW 64 ;# of directory entries RAMSECT1 DW 256 ;Total number of sectors DB 0FEH ;Media descriptor FATSIZE1 DW FATSIZE ;# of sectors used by FAT BPB_PTR DW BPB ;BIOS parameter blk ptr array ; ;***Current virtual disk information ; DB 'SIZE' ; RAMSIZEK DW RAMDISK ; size in 1k increments RAMSIZEP DW RAMDISK*(1024/16) ; size in paragraphs ; TOTAL DW ? ;Total sectors to transfer VERIFY DB 0 ;Verify 1=YES, 0=NO START_SEC DW 0 ;Starting sector number VDISK_PTR DW 0 ;Starting sector of virtual disk USER_DTA DW ?,? ;Pointer to callers DTA BOOT_REC EQU $ ;Dummy DOS boot record DB 3 DUP(0) ;3 byte jump to boot code (not bootable) DB 'IBM 2.0' DW SECT ;Number of bytes in a sector DB 1 ;1 sector per allocation unit DW 1 ;1 reserved sector DB 2 ;2 FATS DW 64 ;Number of directory entries RAMSECT2 DW 256 ; total sectors in image DB 0FEH ;Single sided 9 sector FATSIZE2 DW FATSIZE ;Number of sectors in FAT ; ;***Function table ; FUNTAB LABEL BYTE DW INIT ;Initialization DW MEDIA_CHECK ;Media check (block only) DW BUILD_BPB ;Build BPB DW IOCTL_IN ;IOCTL input DW INPUT ;Input (read) DW ND_INPUT ;Non destructive input no wait (ch only) DW IN_STAT ;Input status DW IN_FLUSH ;Input flush DW OUTPUT ;Output (write) DW OUT_VERIFY ;Output (write) with verify DW OUT_STAT ;Output status DW OUT_FLUSH ;Output flush DW IOCTL_OUT ;IOCTL output ; ;***Local procedures ; IN_SAVE PROC NEAR MOV AX,ES:WORD PTR DTA[BX] ;Save callers DTA MOV CS:USER_DTA,AX MOV AX,ES:WORD PTR DTA+2[BX] MOV CS:USER_DTA+2,AX MOV AX,ES:WORD PTR COUNT[BX] XOR AH,AH MOV CS:TOTAL,AX RET IN_SAVE ENDP ; ; CALC_ADDR PROC NEAR MOV AX,CS:START_SEC ;Get starting sector number MOV CX,SECT/16 ;Move 512 to CX segment style MUL CX ;Multiply to get actual sector MOV DX,CS:VDISK_PTR ;Get segment of virtual disk ADD DX,AX ;Add that seg to initial seg MOV DS,DX ;Save that as actual segment XOR SI,SI ;It's on a para boundary MOV AX,CS:TOTAL ;Total # of sectors to read MOV CX,SECT ;Bytes per sector MUL CX ;Multiply to get copy length OR AX,AX ;Check for greater than 64K JNZ MOVE_IT MOV AX,0FFFFH ;Move in for 64K MOVE_IT: XCHG CX,AX ;Move length to CX RET CALC_ADDR ENDP SECTOR_READ PROC NEAR CALL CALC_ADDR ;Calculate the starting sector MOV ES,CS:USER_DTA+2 ;Set dest (ES:DI) to point MOV DI,CS:USER_DTA ;**to callers DTA ; ;***Check for DTA wrap in case we came through via verify ; MOV AX,DI ;Get offset of DTA ADD AX,CX ;Add copy length to it JNC RC ;Carry flag=0, no wrap MOV AX,0FFFFH ;Max length SUB AX,DI ;Subtract DTA offset from max MOV CX,AX ;Use that as copy length to avoid wrap RC: REP MOVSB ;Do the read RET SECTOR_READ ENDP SECTOR_WRITE PROC NEAR CALL CALC_ADDR ;Calculate starting sector PUSH DS POP ES ;Establish addressability MOV DI,SI ;ES:DI point to disk MOV DS,CS:USER_DTA+2 ;DS:SI point to callers DTA MOV SI,CS:USER_DTA ; ;***Check for DTA wrap ; MOV AX,SI ;Move DTA offset to AX ADD AX,CX ;Add copy length to offset JNC WC ;Carry flag=0, no segment wrap MOV AX,0FFFFH ;Move in max copy length SUB AX,SI ;Subtract DTA offset from max MOV CX,AX ;Use as new copy length to avoid wrap WC: REP MOVSB ;Do the write RET SECTOR_WRITE ENDP PAGE ; ;***Device strategy ; DEV_STATEGY: MOV CS:RH_SEG,ES ;Save segment of request header ptr MOV CS:RH_OFF,BX ;Save offset of " " " RET ; ;***Device interrupt handler ; DEV_INT: CLD ; ;***Save state of machine ; PUSH DS PUSH ES PUSH AX PUSH BX PUSH CX PUSH DX PUSH DI PUSH SI ; ;***Do branch according to function passed ; MOV AL,ES:[BX]+2 ;Get function byte ROL AL,1 ;Get offset into table LEA DI,FUNTAB ;Get address of function table XOR AH,AH ADD DI,AX JMP WORD PTR[DI] ; ;***Initialization ; INIT: MOV AX,CS:RAMSIZEK ; compute size in # sect CMP AX,171 ; JB INIT20 ; MOV CS:WORD PTR FATSIZE1,2 ; MOV CS:WORD PTR FATSIZE2,2 ; CMP AX,341 ; JB INIT20 ; MOV CS:WORD PTR FATSIZE1,3 ; MOV CS:WORD PTR FATSIZE2,3 ; INIT20: ; SHL AX,1 ; MOV CS:RAMSECT1,AX ; MOV CS:RAMSECT2,AX ; MOV CL,5 ; compute size in paragr SHL AX,CL ; MOV CS:RAMSIZEP,AX ; ; PUSH CS POP DX ;Current CS to DX LEA AX,CS:VDISK ;Get address of virtual disk MOV CL,4 ROR AX,CL ;Divide by 16 (paragraph form) ADD DX,AX ;Add to current CS value MOV CS:VDISK_PTR,0D000H ;Save as start seg of vdisk ; MOV AX,CS:RAMSIZEP ;Add size of vdisk in para ; ADD DX,AX ;**segment of virtual disk MOV ES:WORD PTR BR_ADDR_0[BX],0 MOV ES:BR_ADDR_1[BX],DX ;Make that the break address MOV ES:BYTE PTR UNITS[BX],1 ;Number of diskette units LEA DX,BPB_PTR ;Get addr of BPB ptr array MOV ES:BPB_PTR_OFF[BX],DX ;Save offset in data packet MOV ES:BPB_PTR_SEG[BX],CS ;Save segment in data packet find_it: lea si,boot_rec ;point to signature string add si,3 mov ax,0D000H ;where signature should be push es mov es,ax mov di,3 ;offset of signature mov cx,8 ;signature is 8 bytes long cld repe cmpsb ;look for it pop es ;restore ES jcxz its_there ;CX=0 implies we found it jmp cold_start ;otherwise, do full install its_there: jmp hot_start ;PUSH DS ;Save DS ;MOV AX,40H ;BIOS data segment ;MOV DS,AX ;Set up segment register ;CMP WORD PTR DS:[0072H],1234H ;Check reset mode ;POP DS ;Restore DS ;JNZ COLD_START ;Initialize Vdisk ;JMP HOT_START ;Skip if boot from keyboard COLD_START: ; Initialize memory to avoid PARITY errors SUB AX,AX ;Byte to write (null) MOV ES,CS:VDISK_PTR ;Get start addr of 1st 64K sector MOV CX,8000H ;Number of bytes to write REP STOSW ;Repeat no. of times in CX MOV CX,ES ;Move to next sector by ADD CX,1000H ;adding 1000H to ES MOV ES,CX ;New start addr at E000H MOV CX,8000H ;Reset CX counter to 8000H REP STOSW ;Repeat write in second sect MOV ES,CS:VDISK_PTR ;Restore addr to 1st seg XOR DI,DI ;Zero out DI (Boot record) LEA SI,BOOT_REC ;Address of boot record MOV CX,24 REP MOVSB ;Copy 24 bytes of boot record MOV CS:WORD PTR START_SEC,1 MOV AX,FATSIZE1 ; MOV CS:WORD PTR TOTAL,AX ; CALL CALC_ADDR ;Calc addr of logical sect 1 PUSH DS POP ES MOV DI,SI ;Move that address to ES:DI XOR AL,AL REP STOSB ;Zero out FAT area MOV DS:BYTE PTR[SI],0FEH ;Set the first FAT entry MOV DS:BYTE PTR 1[SI],0FFH MOV DS:BYTE PTR 2[SI],0FFH PUSH DS ;Save pointer to FAT on PUSH SI ;**the stack MOV AX,CS:WORD PTR START_SEC ; ADD AX,CS:WORD PTR TOTAL ; MOV CS:WORD PTR START_SEC,AX ; ;DEL MOV CS:WORD PTR TOTAL,2 CALL CALC_ADDR ;Calc addr of logical sect 3 PUSH DS POP ES MOV DI,SI ;Move that address to ES:DI POP SI POP DS ;Restore addr of 1st entry REP MOVSB ;Copy first FAT to second FAT MOV AX,CS:WORD PTR START_SEC ; ADD AX,CS:WORD PTR TOTAL ; MOV CS:WORD PTR START_SEC,AX ; ;DEL MOV CS:WORD PTR START_SEC,5 MOV CS:WORD PTR TOTAL,4 CALL CALC_ADDR ;Calc addr of LS 5 (start dir XOR AL,AL PUSH DS POP ES ;Set up ES:DI to point to it XOR DI,DI REP STOSB ;Zero out directory MOV ES,CS:RH_SEG ;Restore ES:BX to req header MOV BX,CS:RH_OFF STATUS DONE,NOERROR,0 ;Set status done (noerror) LEA DX,COLD_MESSAGE ;Choose initialize msg JMP SHORT MESSAGE ;Go display it COLD_MESSAGE DB 10,13,"Hi-RAM disk installed.",13,10,"$" ; HOT_MESSAGE DB 10,13,"Hi RAM disk preserved.",13,10,"$" ; HOT_START: LEA DX,HOT_MESSAGE ;Choose preserve msg MESSAGE: PUSH CS ;Current segment POP DS ;Set DS to it MOV AH,9 ;DOS print string call INT 21H ;Print the string JMP EXIT ; ;***Media check ; MEDIA_CHECK: MOV ES:BYTE PTR RET_BYTE[BX],1 STATUS DONE,NOERROR,0 ;turn on the done bit JMP EXIT ; ;***Build BIOS parameter block ; BUILD_BPB: PUSH ES ;Save SRH segment PUSH BX ;Save SRH offset MOV CS:WORD PTR START_SEC,0 MOV CS:WORD PTR TOTAL,1 CALL CALC_ADDR ;Calc addr of 1st sector PUSH CS POP ES LEA DI,BPB ;Addr of BIOS parameter block ADD SI,11 MOV CX,13 ;Length of BPB REP MOVSB POP BX ;Restore offset of SRH POP ES ;Restore segment of SRH LEA DX,BPB ;Get BPB array pointer MOV ES:BPBA_PTR[BX],DX ;Save ptr to BPB table MOV ES:BPBA_PTR+2[BX],CS MOV ES:DTA[BX],DX ;Offset to sector buffer MOV ES:DTA+2[BX],CS STATUS DONE,NOERROR,0 JMP EXIT ; ;***The following entries are not supported by this device ; IOCTL_IN: IOCTL_OUT: ND_INPUT: ;Non destructive input no wait (ch only) IN_STAT: ;Input status " " IN_FLUSH: ;Input flush OUT_STAT: ;Output status OUT_FLUSH: ;Output flush ; ;***Disk read ; INPUT: CALL IN_SAVE ;Call the initial save rtn MOV AX,ES:WORD PTR SSN[BX] ;Set starting sector number MOV CS:START_SEC,AX ;Save starting sector number MOV AX,ES:WORD PTR COUNT[BX] MOV CS:TOTAL,AX ;Save # sectors to transfer CALL SECTOR_READ ;Read in that many sectors MOV BX,CS:RH_OFF ;Restore ES:BX as req header MOV ES,CS:RH_SEG STATUS DONE,NOERROR,0 ;Set status word (done, noerr JMP EXIT ; ;***Disk write ; OUTPUT: CALL IN_SAVE ;Output (write) MOV AX,ES:WORD PTR SSN[BX] ;Get starting sector number MOV CS:START_SEC,AX ;Set " " " MOV AX,ES:WORD PTR COUNT[BX] MOV CS:TOTAL,AX ;Save total sectors to write CALL SECTOR_WRITE MOV BX,CS:RH_OFF ;Restore ES:BX as req header MOV ES,CS:RH_SEG CMP CS:BYTE PTR VERIFY,0 ;Write verify set JZ NO_VERIFY ;No, no write verify MOV CS:BYTE PTR VERIFY,0 ;Reset verify indicator JMP INPUT NO_VERIFY: STATUS DONE,NOERROR,0 ;Set status done, no error JMP EXIT OUT_VERIFY: MOV CS:BYTE PTR VERIFY,1 ;Set verify flag JMP OUTPUT ;Branch to output routine ; ;***Common exit ; EXIT: POP SI POP DI POP DX POP CX POP BX POP AX POP ES POP DS RET ; ; E_O_P: ;***Macro to align the virtual disk on a paragraph boundary IF ($-START) MOD 16 ORG ($-START)+16-(($-START) MOD 16) ENDIF VDISK EQU $ VDSK ENDP CSEG ENDS end begin