;JS note, 8/1/83: This version of the variable-size virtual disk ; can handle up to 112 files. There seems not to be any need ; to change to a "double-sided" drive, but one must take care ; to zero out 7 sectors for the directory, rather than just the usual 4. PAGE 66,132 ; ; ****************************************************** ; * PROLOG * ; * THIS IS AN INSTALLABLE DEVICE DIVER FOR AN * ; * IN STORAGE DISKETTE (VIRTUAL) WITH 180K CAPACITY. * ; ****************************************************** CSEG SEGMENT PARA PUBLIC 'CODE' ; ; M A C R O ( S ) ; 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],0100H ENDIF IFNB OR ES:WORD PTR SRH_STA_FLD[BX],RC ENDIF ENDM ; ; E Q U A T E S ; ; READ/WRITE ; SRH EQU 0 ;STATIC REQUEST HEADER START SRH_LEN EQU 13 ; " " " LENGTH SRH_LEN_FLD EQU SRH ; " " " " FIELD SRH_UCD_FLD EQU SRH+1 ; " " " UNIT CODE FIELD SRH_CCD_FLD EQU SRH+2 ; " " " COMMAND CODE FIELD SRH_STA_FLD EQU SRH+3 ; " " " STATUS FIELD SRH_RES_FLD EQU SRH+5 ; " " " RESERVED AREA FIELD ; 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 ;POINTER TO BPB BPBA_PTR_LEN EQU 4 ; " " " LENGTH ; ; 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 $ ; S P E C I A L D E V I C E H E A D E R NEXT_DEV DD -1 ;POINTER TO NEXT DEVICE ATTRIBUTE DW 2000H ;BLOCK DEVICE (NON-IBM FORMAT) STRATEGY DW DEV_STRATEGY ;POINTER TO DEVICE STRATEGY 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 512 ;SECTOR SIZE DB 2;(JS change) ;SECTOR/ALLOCATION UNIT DW 1 ;NUMBER OF RESERVED SECTORS DB 2 ;NUMBER OF FATS DW 112;(JS change) ;NUMBER OF DIRECTORY ENTRIES SECTCT1 DW ? ;TOTAL NUMBER OF SECTORS DB 0FCH; ;MEDIA DESCRIPTOR DW 2 ;NUMBER OF SECTORS OCCUPIED BY FAT ; BPB_PTR DW BPB ;BIOS PARAMETER BLOCK POINTER ARRAY (1 ENTR) ; CURRENT VIRTUAL DISK INFORMATION 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 SEGMENT OF VIRTUAL DISK USER_DTA DD ? ;POINTER TO CALLSER DISK TRANSFER ADDRESS BOOT_REC EQU $ ;DUMMY DOS BOOT RECORD DB 3 DUP (0) ;3 BYTE JUMP TO BOOT CODE (NOT BOOTABLE) DB 'IBM 2.0' ;VENDOR IDENTIFICATION DW 512 ;NUMBER OF BYTES IN A SECTOR DB 2;(JS change) ;1 SECTOR PER ALLOCATION UNIT DW 1 ;1 RESERVED SECTOR DB 2 ;2 FATS DW 112;(JS change) ;NUMBER OF DIRECTORY ENTRIES SECTCT2 DW ? ;TOTAL SECTORS IN IMAGE DB 0FCH; ;TELLS DOS THIS IS A SINGLE SIDED 9 SECTOR DW 2 ;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 (CHAR ONLY) DW IN_STAT ;INPUT STATUS (CHAR ONLY) DW IN_FLUSH ;INPUT FLUSH " " DW OUTPUT ;OUTPUT (WRITE) DW OUT_VERIFY ;OUTPUT (WRITE) WITH VERIFY DW OUT_STAT ;OUTPUT STATUS (CHAR ONLY) DW OUT_FLUSH ;OUTPUT FLUSH " " DW IOCTL_OUT ;IOCTL OUTPUT ; ; L O C A L P R O C E D U R E S ; 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] ;GET NUMBER OF SECTOR TO TOTAL XOR AH,AH MOV CS:TOTAL,AX ;MOVE NUMBER OF SECTORS TO TOTAL RET IN_SAVE ENDP ; CALC_ADDR PROC NEAR MOV AX,CS:START_SEC ;GET STARTING SECTOR NUMBER MOV CX,20H ;MOV 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 SEGMENT TO INITIAL SEGMENT MOV DS,DX ;SAVE THAT AS THE ACTUAL SEGMENT XOR SI,SI ;IT'S ON A PARAGRAPH BOUNDRY MOV AX,CS:TOTAL ;TOTAL NUMBER OF SECTOR TO READ MOV CX,512 ;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 DESTINATION (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 READ_COPY ;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 READ_COPY: 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 WRITE_COPY ;CARRY FLAG = 0, NO SEGMENT WRAP MOV AX,0FFFFH ;MOVE IN MAX COPY LENGTH SUB AX,SI ;SUTRACT DTA OFFSET FROM MAX MOV CX,AX ;USE AS NEW COPY LENGTH TO AVOID WRAP WRITE_COPY: REP MOVSB ;DO THE 'WRITE' RET SECTOR_WRITE ENDP ; ; D E V I C E S T R A T E G Y ; DEV_STRATEGY: MOV CS:RH_SEG,ES ;SAVE SEGMENT OF REQUEST HEADER POINTER MOV CS:RH_OFF,BX ;SAVE OFFSET OF " "... RET ; ; D E V I C E I N T E R R U P T H A N D L E R ; DEV_INT: ; PRESERVE MACHINE STATE ON ENTRY CLD PUSH DS PUSH ES PUSH AX PUSH BX PUSH CX PUSH DX PUSH DI PUSH SI ; ; DO THE BRANCH ACCORDING TO THE 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 MOV BX,CS:RH_OFF ;See PC Age 2.6, MOV ES,CS:RH_SEG ; p. 65. (JS) JMP WORD PTR[DI] ; ; INIT ; INIT: PUSH CS POP DX ;CURRENT CS TO DX ; PUSH ES ;JS modification: MOV AX,0 ; Load number of kilobytes MOV ES,AX ; from last half of last MOV AX,ES:3FEH ; "interrupt" slot, convert SAL AX,1 ; to number of sectors, MOV CS:SECTCT1,AX ; and store result. MOV CS:SECTCT2,AX POP ES ; 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,DX ;SAVE AS STARTING SEGMENT OF VIRTUAL DISK MOV AX,SECTCT1 ; ADD ENOUGH PARAGRAPHS TO STARTING MOV CL,5 ; SEGMENT OF VIRTUAL DISK SAL AX,CL ADD DX,AX 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 ADDRESS OF BPB POINTER 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 MOV ES,CS:VDISK_PTR ;GET STARTING SECTOR OF VIRTUAL DISK 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 CS:WORD PTR TOTAL,2 CALL CALC_ADDR ;CALCULATE ADDRESS OF LOGICAL SECTOR 1 PUSH DS POP ES MOV DI,SI ;MOVE THAT ADDRES TO ES:DI XOR AL,AL REP STOSB ;ZERO OUT FAT AREA MOV DS:BYTE PTR [SI],0FCH ;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 PUSH SI ; ON THE STACK MOV CS:WORD PTR START_SEC,3 MOV CS:WORD PTR TOTAL,2 CALL CALC_ADDR ;CALCULATE ADDRESS OF LOGICAL SECTOR 3 PUSH DS POP ES MOV DI,SI ;MOVE THAT ADDRESS TO ES:DI POP SI POP DS ;RESTORE ADDRESS TO FIRST FAT REP MOVSB ;COPY FIRST FAT TO SECOND FAT MOV CS:WORD PTR START_SEC,5 MOV CS:WORD PTR TOTAL,7;(JS change) CALL CALC_ADDR ;CALCULATE ADDR OF L.S. 5 (START OF DIR) XOR AL,AL PUSH DS POP ES ;SET UP ES:DI TO POINT T XOR DI,DI REP STOSB ;ZERO OUT DIRECTORY MOV ES,CS:RH_SEG ;RESTORE ES:BX TO REQUEST HEADER MOV BX,CS:RH_OFF STATUS DONE,NOERROR,0 ;SET STATUS WORD (DONE, NOERROR) JMP EXIT ; ; MEDIA CHECK ; MEDIA_CHECK: ;MEDIA CHECK (BLOCK ONLY) ; ; SET MEDIA NOT CHANGED ; MOV ES:BYTE PTR RET_BYTE[BX],1 ;STORE IN RETURN BYTE STATUS DONE,NOERROR,0 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 PUSH CS POP ES LEA DI,BPB ;ADDRESS OF BIOS PARAMETER BLOCK ADD SI,11 ;ADD 11 TO SI 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 OF SECTOR BUFFER MOV ES:DTA+2[BX],CS STATUS DONE,NOERROR,0 JMP EXIT ; ; THE FOLLOWING ENTRIES ARE FOR NOT SUPPORTED BY THIS DEVICE ; IOCTL_IN: IOCTL_OUT: ND_INPUT: ;NON_DESTRUCTIVE INPUT NO WAIT (CHAR 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 ROUTINE 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 TOTAL SECTORS TO TRANSFER CALL SECTOR_READ ;READ IN THAT MANY SECTORS MOV BX,CS:RH_OFF ;RESTORE ES:BX AS REQUEST HEADER POINTER MOV ES,CS:RH_SEG STATUS DONE,NOERROR,0 JMP EXIT ; ; DISK WRITE ; OUTPUT: ;OUTPUT (WRITE) CALL IN_SAVE 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 ;WRITE OUT THOSE SECTORS MOV BX,CS:RH_OFF ;RESTORE ES:BX AS REQUEST HEADER POINTER 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 ;READ THOSE SECTORS BACK IN NO_VERIFY: STATUS DONE,NOERROR,0 ;SET DONE, NO EROR IN STATUS WORD JMP EXIT OUT_VERIFY: ;OUTPUT (WRITE) WITH VERIFY MOV CS:BYTE PTR VERIFY,1 ;SET THE VERIFY FLAG JMP OUTPUT ;BRANCH TO OUTPUT ROUTINE ; ; COMMON EXIT ; EXIT: POP SI ;RESTORE ALL OF THE REGISTERS 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