home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Boston 2
/
boston-2.iso
/
DOS
/
HILFEN
/
SYSTEM
/
RAMDISK4
/
XPANDISK.ASM
< prev
next >
Wrap
Assembly Source File
|
1993-12-01
|
38KB
|
864 lines
; XpanDisk is a virtual disk device driver for expanded memory.
; The driver has the capability of releasing memory allocated to
; its drive, making it available to other applications. All data
; is lost if the drive is resized. XPANBOSS.COM is the control
; program for the XPANDISK.SYS driver. PC Magazine 10-31-88
_TEXT SEGMENT PUBLIC 'CODE'
ASSUME CS:_TEXT,DS:_TEXT
ASSUME ES:_TEXT,SS:_TEXT
ORG 0H
;************* DEVICE_HEADER *************;
POINTER DD -1 ;Minus one indicates one driver.
ATTRIBUTE DW 0100000000000000B ;Block device with IOCTL support.
DEVICE_STAG DW STRATEGY ;Pointer to strategy procedure.
DEVICE_INT DW INTERRUPT ;Pointer to interrupt procedure.
DEVICE_NAME DB 1, 7 DUP (?) ;One unit.
REQUEST_HEADER STRUC
HEADER_LENGTH DB ?
UNIT_CODE DB ?
COMMAND_CODE DB ?
STATUS DW ?
RESERVED DQ ?
REQUEST_HEADER ENDS
INIT_PACKET STRUC
INIT_HEADER DB (TYPE REQUEST_HEADER) DUP(?)
UNITS DB ?
ENDING_OFFSET DW ?
ENDING_SEGMENT DW ?
ARGUMENTS_OR_ARRAY_OFF DW ?
ARGUMENTS_OR_ARRAY_SEG DW ?
INIT_PACKET ENDS
MEDIA_CHECK_PACKET STRUC
MEDIA_CHECK_HEADER DB (TYPE REQUEST_HEADER) DUP(?)
CHECK_MEDIA_DESCRIPTOR DB ?
RETURN_BYTE DB ?
MEDIA_CHECK_PACKET ENDS
BUILD_BPB_PACKET STRUC
BUILD_BPB_HEADER DB (TYPE REQUEST_HEADER) DUP(?)
BPB_MEDIA_DESCRIPTOR DB ?
BPB_TRANSFER_ADDRESS DD ?
BPB_OFFSET DW ?
BPB_SEGMENT DW ?
BUILD_BPB_PACKET ENDS
INPUT_OUTPUT_PACKET STRUC
INPUT_OUTPUT_HEADER DB (TYPE REQUEST_HEADER) DUP(?)
IO_MEDIA_DESCRIPTOR DB ?
TRANSFER_OFFSET DW ?
TRANSFER_SEGMENT DW ?
BYTE_OR_SECTOR_COUNT DW ?
STARTING_SECTOR DW ?
INPUT_OUTPUT_PACKET ENDS
CR EQU 13
LF EQU 10
CTRL_Z EQU 26
SPACE EQU 32
BOX EQU 254
DONE EQU 0100H ;STATUS CODES
WRITE_PROTECT EQU 8000H
UNKNOWN_UNIT EQU 8001H
DEVICE_NOT_READY EQU 8002H
UNKNOWN_COMMAND EQU 8003H
SECTOR_NOT_FOUND EQU 8008H
MAX_COMMAND EQU 12
EMM_FLAG DB 1
REQUEST_PACKET LABEL DWORD
REQUEST_OFFSET DW ?
REQUEST_SEG DW ?
;-----------------------------------------------------------------------------;
; The only task of the strategy is to save the pointer to the request header. ;
;-----------------------------------------------------------------------------;
STRATEGY PROC FAR
MOV CS:REQUEST_OFFSET,BX ;Request header address is
MOV CS:REQUEST_SEG,ES ; passed in ES:BX.
RET
STRATEGY ENDP
;------------------------------------------------------------------------;
; The interrupt procedure will be called immediately after the strategy. ;
;------------------------------------------------------------------------;
INTERRUPT PROC FAR
PUSH AX ;Responsible for all registers.
PUSH BX
PUSH CX
PUSH DX
PUSH DS
PUSH ES
PUSH SI
PUSH DI
PUSH BP
PUSHF
CLD ;All string moves forward.
PUSH CS ;Point to our data.
POP DS
CMP EMM_FLAG,1 ;Was EMM found?
MOV AX,UNKNOWN_UNIT ;Assume no.
JNZ EXIT ;If no expanded manager, exit.
LES DI,REQUEST_PACKET ;Retrieve request header pointer.
MOV BL,ES:COMMAND_CODE[DI] ;Retrieve command.
CMP BL,MAX_COMMAND ;Do we support it?
MOV AX,UNKNOWN_COMMAND ;Assume no.
JA EXIT ;If out of range, exit.
XOR BH,BH ;Zero in high half of command.
SHL BX,1 ;Convert to word pointer.
CALL DISPATCH_TABLE[BX] ;Go do our thing.
EXIT: LDS DI,REQUEST_PACKET ;Retrieve request header pointer.
OR AX,DONE
MOV STATUS[DI],AX ;Tell DOS we are done.
POPF ;Restore rest of registers.
POP BP ;Restore registers.
POP DI
POP SI
POP ES
POP DS
POP DX
POP CX
POP BX
POP AX
RET ;Far return back to DOS.
INTERRUPT ENDP
EVEN
MIN_RESIDENT EQU $
DISPATCH_TABLE LABEL WORD
DW INIT, MEDIA_CHECK, BUILD_BPB, IOCTL_INPUT, INPUT
DW INPUT_NOWAIT, INPUT_STATUS, INPUT_FLUSH, OUTPUT
DW OUTPUT_VERIFY, OUTPUT_STATUS, OUTPUT_FLUSH, IOCTL_OUTPUT
BOOT_RECORD LABEL BYTE
DB 3 DUP (0)
DB "XPANDISK"
BIOS_PARAMETER_BLOCK LABEL BYTE
BYTES_PER_SECTOR DW SECTOR_SIZE_DEFAULT
SECTORS_PER_CLUSTER DB 1
RESERVED_SECTORS DW 1
NUMBER_OF_FATS DB 1
ROOT_DIRECTORY_ENTRIES DW ROOT_ENTRIES_DEFAULT
TOTAL_SECTORS DW 1024 / SECTOR_SIZE_DEFAULT * DISK_SIZE_DEFAULT
MEDIA_DESCRIPTOR_BYTE DB MEDIA_DESCRIPTOR
SECTORS_PER_FAT DW 1
SECTORS_PER_TRACK DW 8
NUMBER_OF_HEADS DW 1
HIDDEN_SECTORS DW 0
BOOT_RECORD_LENGTH EQU $ - BOOT_RECORD
MEDIA_DESCRIPTOR EQU 0FEH
DISK_SIZE_DEFAULT EQU 64
DISK_SIZE_MINIMUM EQU 16
DISK_SIZE_MAXIMUM EQU 32 * 1024
SECTOR_SIZE_DEFAULT EQU 256
SECTOR_SIZE_MINIMUM EQU 128
SECTOR_SIZE_MAXIMUM EQU 512
ROOT_ENTRIES_DEFAULT EQU 64
ROOT_ENTRIES_MINIMUM EQU 4
ROOT_ENTRIES_MAXIMUM EQU 512
DISK_SIZE_TEMP DW DISK_SIZE_DEFAULT
SECTOR_SIZE_TEMP DW SECTOR_SIZE_DEFAULT
ROOT_ENTRIES_TEMP DW ROOT_ENTRIES_DEFAULT
DISK_SIZE DW DISK_SIZE_DEFAULT
PARA_FLAG DB ?
READ_ONLY_FLAG DB 0
FORMAT_FLAG DB 1
BPB_ARRAY DW BIOS_PARAMETER_BLOCK
VOLUME_LABEL LABEL BYTE
DB "PCMAG",SPACE,BOX,SPACE,"MJM"
DB 28H
DT 0
DW 6000H ;Date July 1, 1988
DW 10E1H ;Time 12:00pm
DB 6 DUP (0)
VOLUME_LENGTH EQU $ - VOLUME_LABEL
PASSWORD DB "PC Magazine Productivity"
PASSWORD_LENGTH EQU $ - PASSWORD
DISK_SIZE_MSG DB CR,LF,"Disk Size ",0
SECTOR_SIZE_MSG DB "K",CR,LF,"Sector Size ",0
DIRECTORY_ENTRIES_MSG DB CR,LF,"Directory Entries ",0
MEDIA_NOT_CHANGED EQU 1
MEDIA_CHANGED EQU -1
TWELVE_BIT_FAT EQU 4087
ONE_K EQU 1024
SIXTEEN_K EQU 16384
THIRTY_TWO_K EQU 32768
INPUT_COMMAND EQU 4
COMMAND DB ?
FRAME_SEGMENT DW ?
HANDLE DW ?
PAGES DW ?
;--------------------------------------------------------------;
; Character device functions; ignore and return no error code. ;
;--------------------------------------------------------------;
INPUT_NOWAIT:
INPUT_STATUS:
INPUT_FLUSH:
OUTPUT_STATUS:
OUTPUT_FLUSH: XOR AX,AX ;Successful.
RET
;------------------------------------------------------;
; Tell DOS media has changed if disk has been resized. ;
;------------------------------------------------------;
MEDIA_CHECK PROC NEAR
MOV ES:RETURN_BYTE[DI],MEDIA_NOT_CHANGED
CMP FORMAT_FLAG,1
JNZ MEDIA_END
MOV ES:RETURN_BYTE[DI],MEDIA_CHANGED
MEDIA_END: XOR AX,AX
RET
MEDIA_CHECK ENDP
;---------------------------------------------------------------;
; When DOS has retrieved BPB, indicate media no longer changed. ;
;---------------------------------------------------------------;
BUILD_BPB PROC NEAR
MOV ES:BPB_OFFSET[DI],OFFSET BIOS_PARAMETER_BLOCK
MOV ES:BPB_SEGMENT[DI],CS
MOV FORMAT_FLAG,0
XOR AX,AX
RET
BUILD_BPB ENDP
;---------------------------------------------------------------;
; Entry point for both DOS INPUT and OUTPUT. The direction ;
; the data is moved is determined by INPUT/OUTPUT command code. ;
;---------------------------------------------------------------;
INPUT_OUTPUT PROC NEAR
INPUT:
OUTPUT:
OUTPUT_VERIFY: MOV AL,ES:COMMAND_CODE[DI]
MOV COMMAND,AL ;Save the DOS request.
;; If you add this code, an asterisk will be
;; displayed so you can observe every disk access.
;; push ax
;; mov al,"*"
;; call write_tty
;; pop ax
CMP READ_ONLY_FLAG,1 ;Is this a write protect disk?
JNZ CK_RANGE ;If no, go check range.
CMP AL,INPUT_COMMAND ;Else, is this an write request?
MOV AX,WRITE_PROTECT ;Assume yes; write violation.
JNZ IO_ERROR ;If guessed right, exit.
CK_RANGE: MOV CX,ES:BYTE_OR_SECTOR_COUNT[DI] ;Retrieve sector count.
MOV BP,ES:STARTING_SECTOR[DI] ;Retrieve starting sector
MOV BX,BP ; and save in BX.
CMP BP,TOTAL_SECTORS ;If starting sector greater
MOV AX,SECTOR_NOT_FOUND ; or equal to total sectors
JAE IO_ERROR ; then out of range.
ADD BX,CX ;If starting + count > total
CMP BX,TOTAL_SECTORS ; then out of range.
JBE SAVE_PAGE_MAP
IO_ERROR: MOV ES:BYTE_OR_SECTOR_COUNT[DI],0 ;No sectors moved.
RET
SAVE_PAGE_MAP: MOV DX,HANDLE ;Retrieve EMM handle.
MOV AH,47H ;Preserve current expanded
INT 67H ; memory registers.
OR AH,AH ;Was there an error?
MOV AX,DEVICE_NOT_READY ;If yes, return error.
JNZ IO_ERROR
PUSH DS ;Preserve data segment.
MOV DX,BYTES_PER_SECTOR ;Retrieve bytes/sector.
MOV AX,ES:TRANSFER_SEGMENT[DI] ;Destination, transfer
MOV SI,ES:TRANSFER_OFFSET[DI] ; segment and offset.
MOV BX,FRAME_SEGMENT ;Source EMM segment.
CMP COMMAND,INPUT_COMMAND ;Is this a DOS read request?
JNZ SET_SEGMENTS ;If no, write; guessed right.
XCHG AX,BX ;Else, read request; swap
MOV DI,SI ; source and destination.
SET_SEGMENTS: MOV DS,AX ;Set segments.
MOV ES,BX
MOV BX,-1 ;Initialize page out of range.
NEXT_SECTOR: PUSH CX ;Save sector count.
PUSH DX ;Save bytes/sector.
MOV AX,BP ;Requested sector times
MUL DX ; bytes/sector = bytes offset.
MOV CX,SIXTEEN_K ;Divide by 16K; quotient = page
DIV CX ; remainder = offset.
CMP AX,BX ;Is request page currently in
JZ MOVE_SECTOR ; memory? If yes, continue.
MOV BX,AX ;Make requested current page.
CMP CS:COMMAND,INPUT_COMMAND ;Is it a read request?
JNZ OUTPUT_CODE ;If no, write request.
MOV SI,DX ;If read, SI = destination.
JMP SHORT MAP_PAGE
OUTPUT_CODE: MOV DI,DX ;If write, DI = destination.
MAP_PAGE: XOR AL,AL ;AL = physical page; BX = logical
MOV DX,CS:HANDLE ;DX = EMM handle.
MOV AH,44H ;Map the page into memory.
INT 67H
MOVE_SECTOR: POP DX ;Retrieve bytes/sector.
MOV CX,DX ;Save in counter.
SHR CX,1 ;Divide by two.
REP MOVSW ;Move CX words.
INC BP ;Next requested sector.
POP CX ;Retrieve sector count.
LOOP NEXT_SECTOR ;Do all requested sectors.
POP DS ;Restore data segment.
MOV DX,HANDLE ;EMM handle.
MOV AH,48H ;Restore Page Map.
INT 67H
XOR AX,AX ;Return success code.
RET
INPUT_OUTPUT ENDP
;--------------------------------------------------;
; Return password and parsing results to XPANBOSS. ;
;--------------------------------------------------;
IOCTL_INPUT PROC NEAR
MOV CX,PASSWORD_LENGTH ;Is password length same
CMP CX,ES:BYTE_OR_SECTOR_COUNT[DI] ; as byte count request?
JZ IOCTL_WRITE ;If yes, continue.
MOV ES:BYTE_OR_SECTOR_COUNT[DI],0 ;Else, zero transferred.
MOV AX,UNKNOWN_UNIT ;Unknown unit.
JMP SHORT IO_INPUT_END
IOCTL_WRITE: PUSH DS ;Save segments and
PUSH ES ; and counter.
PUSH CX
MOV DS,ES:TRANSFER_SEGMENT[DI] ;Point to XPANBOSS's
MOV SI,81H ; PSP command line.
CALL PARSE ;Parse the parameters.
POP CX ;Restore registers.
POP ES
POP DS
MOV SI,OFFSET PASSWORD ;Point to password.
MOV AX,ES:TRANSFER_OFFSET[DI] ;Destination, XPANBOSS
MOV ES,ES:TRANSFER_SEGMENT[DI] ; communication pipe.
MOV DI,AX
REP MOVSB ;Transfer the password.
MOV AL,PARA_FLAG ;Parameter found?
AND AX,BP ;WITHOUT confirmation flag.
STOSB ;Return 1 if should prompt user.
XOR AX,AX ;Successful.
IO_INPUT_END: RET
IOCTL_INPUT ENDP
;--------------------------------------------------------------------------;
; If counter password confirms, then XPANBOSS OK'ed disk parameter changes.;
;--------------------------------------------------------------------------;
IOCTL_OUTPUT PROC NEAR
MOV CX,PASSWORD_LENGTH ;Is password length same
CMP CX,ES:BYTE_OR_SECTOR_COUNT[DI] ; as byte count request?
JNZ IOCTL_ERROR ;If no, not XPANBOSS.
PUSH ES ;Save request header
PUSH DI ; pointers.
MOV SI,OFFSET PASSWORD ;Check if counter
MOV AX,ES:TRANSFER_OFFSET[DI] ; password confirms.
MOV ES,ES:TRANSFER_SEGMENT[DI]
MOV DI,AX
REPZ CMPSB
POP DI ;Restore registers.
POP ES
JNZ IOCTL_ERROR ;If bad password, exit.
CMP PARA_FLAG,1 ;Any parameters?
JNZ IOCTL_DONE ;If no, our task done.
MOV DX,HANDLE ;Retrieve EMM handle.
MOV AH,45H ;Deallocate Pages.
INT 67H
CALL ALLOCATE ;Allocate new pages.
JC IOCTL_ERROR ;Error exit if failed.
IOCTL_DONE: CALL STATISTICS ;Else, display stats.
XOR AX,AX ;Return successful.
JMP SHORT IO_OUTPUT_END
IOCTL_ERROR: MOV ES:BYTE_OR_SECTOR_COUNT[DI],0 ;Else, zero transferred.
MOV AX,UNKNOWN_UNIT ;Unknown unit.
IO_OUTPUT_END: RET
IOCTL_OUTPUT ENDP
;-------------------------------;
; INPUT - DS:SI Points to parameters.
;-------------------------------;
PARSE PROC NEAR
PUSH CS ;Point ES to local data.
POP ES
MOV ES:PARA_FLAG,0 ;Initialize variables.
MOV ES:READ_ONLY_FLAG,0
MOV ES:DISK_SIZE_TEMP,DISK_SIZE_DEFAULT
MOV ES:SECTOR_SIZE_TEMP,SECTOR_SIZE_DEFAULT
MOV ES:ROOT_ENTRIES_TEMP,ROOT_ENTRIES_DEFAULT
MOV BP,1 ;WITHOUT asking flag; assume yes.
NEXT_PARSE: LODSB ;Get a byte.
CMP AL,CR ;If carriage return
JZ PARSE_END ; or linefeed, done.
CMP AL,LF
JZ PARSE_END
CMP AL,"/" ;Switch character?
JNZ NEXT_PARSE ;If no, next byte.
LODSB ;Else, get switch.
CMP AL,CR ;If CR or LF, done.
JZ PARSE_END
CMP AL,LF
JZ PARSE_END
AND AL,5FH ;Else, capitalize
CMP AL,"M" ;Is it Minimum?
JNZ CK_A
MOV ES:DISK_SIZE_TEMP,DISK_SIZE_MINIMUM
JMP SHORT PARA_CHANGED
PARSE_END: RET
CK_A: CMP AL,"A" ;Is it Maximum?
JNZ CK_R
MOV ES:DISK_SIZE_TEMP,DISK_SIZE_MAXIMUM
JMP SHORT PARA_CHANGED
CK_R: CMP AL,"R" ;Is it Read-only?
JNZ CK_W
MOV ES:READ_ONLY_FLAG,1
CK_W: CMP AL,"W" ;Is it WITHOUT asking?
JNZ CK_PARA
XOR BP,BP ;WITHOUT confirmation flag.
CK_PARA: CALL DECIMAL_INPUT ;Get decimal number.
CMP AL,"D" ;Is switch character Disk Size?
JNZ CK_S
CMP BX,DISK_SIZE_MINIMUM ;If yes, check boundaries.
JAE CK_D_MAX
MOV BX,DISK_SIZE_MINIMUM
CK_D_MAX: CMP BX,DISK_SIZE_MAXIMUM
JBE STORE_D_TEMP
MOV BX,DISK_SIZE_MAXIMUM
STORE_D_TEMP: MOV ES:DISK_SIZE_TEMP,BX
JMP SHORT PARA_CHANGED
CK_S: CMP AL,"S" ;Is it Sector Size?
JNZ CK_E
MOV CX,SECTOR_SIZE_MINIMUM ;If yes, check boundaries.
CMP BX,CX
JBE STORE_S_TEMP
MOV CX,SECTOR_SIZE_MAXIMUM
CMP BX,CX
JAE STORE_S_TEMP
MOV CX,SECTOR_SIZE_DEFAULT
STORE_S_TEMP: MOV ES:SECTOR_SIZE_TEMP,CX
JMP SHORT PARA_CHANGED
CK_E: CMP AL,"E" ;Is it Root Directory Entries?
JNZ PARA_END
CMP BX,ROOT_ENTRIES_MINIMUM ;If yes, check boundaries
JAE CK_E_MAX
MOV BX,ROOT_ENTRIES_MINIMUM
CK_E_MAX: CMP BX,ROOT_ENTRIES_MAXIMUM
JBE STORE_E_TEMP
MOV BX,ROOT_ENTRIES_MAXIMUM
STORE_E_TEMP: MOV ES:ROOT_ENTRIES_TEMP,BX
PARA_CHANGED: OR ES:PARA_FLAG,1 ;Flag that parameter found.
PARA_END: JMP NEXT_PARSE ;Parse all parameters.
PARSE ENDP
;---------------------------------;
; INPUT - SI points to parameter start.
; OUTPUT - SI points to parameter end. BX = number.
;---------------------------------;
DECIMAL_INPUT PROC NEAR
PUSH AX ;Save switch character.
XOR BX,BX ;Start with zero as number.
NEXT_DECIMAL: LODSB ;Get a character.
CMP AL,CR ;Carriage return or linefeed?
JZ ADJUST_DEC ;If yes, done here.
CMP AL,LF
JZ ADJUST_DEC
CMP AL,"/" ;Forward slash?
JZ ADJUST_DEC ;If yes, done here.
SUB AL,"0" ;ASCII to binary.
JC NEXT_DECIMAL ;If not between 0 and 9, skip.
CMP AL,9
JA NEXT_DECIMAL
CBW ;Convert byte to word.
XCHG AX,BX ;Swap old and new number.
MOV CX,10 ;Shift to left by multiplying
MUL CX ; last entry by ten.
JC DECIMAL_ERROR ;If carry, too big.
ADD BX,AX ;Add new number and store in BX.
JNC NEXT_DECIMAL ;If not carry, next number.
DECIMAL_ERROR: MOV BX,-1 ;Else, too big; return -1.
ADJUST_DEC: DEC SI ;Adjust pointer.
POP AX ;Restore switch character.
RET
DECIMAL_INPUT ENDP
;---------------------------;
; OUTPUT - CY = 0 If successful. CY = 1 If unsuccessful.
;---------------------------;
ALLOCATE PROC NEAR
PUSH ES ;Save request header pointers.
PUSH DI
MOV BX,DISK_SIZE_TEMP ;Retrieve disk size request.
ADD BX,15 + 16 ;Round up and adjust for DEC.
MOV CL,4 ;Convert to number of 16K pages.
SHR BX,CL
NEXT_ALLOCATE: DEC BX ;Decrement page request until
JNZ GET_MEMORY ; filled with memory available.
STC ;Error if no memory available.
JMP ALLOCATE_END
GET_MEMORY: MOV AH,43H ;Allocate Pages.
INT 67H
OR AH,AH ;Successful?
JNZ NEXT_ALLOCATE ;If no, decrement by one page.
MOV PAGES,BX ;Store pages.
MOV HANDLE,DX ;Store new EMM handle.
SHL BX,CL ;Convert back to K bytes.
MOV DISK_SIZE,BX ;Store size of disk for stats.
MOV BP,SECTOR_SIZE_TEMP ;Retrieve sector size request.
NEXT_SECTORS: MOV AX,ONE_K ;Disk size * 1024 / sector size
MUL BX ; = total sectors. Total sectors
SUB AX,BP ; cannot exceed 64K. Adjust
SBB DX,0 ; sector size up until total
CMP DX,BP ; sectors <= 64K.
JB GOT_SECTORS ; (Decrement by one sector to
SHL BP,1 ; make division by zero easy
JMP SHORT NEXT_SECTORS ; to detect and avoid.)
GOT_SECTORS: DIV BP ;Divide to get total sectors - 1.
INC AX ;Increment to get total sectors.
OR AX,AX ;Can't have 65536 (64K) sectors.
JNZ SECTORS ;That is, sector total is not
DEC AX ; a zero based number.
DEC AX
SECTORS: MOV TOTAL_SECTORS,AX ;Store sector count.
PUSH AX ;Save results.
MOV BYTES_PER_SECTOR,BP ;Store bytes/sector.
MOV DX,BP ;Retrieve sector size.
MOV CL,5 ;Divide by 32 = entries/sector.
SHR DX,CL
MOV AX,ROOT_ENTRIES_TEMP ;Retrieve entries requested.
DIV DL ;Divide by entries/sector.
ADD AH,-1 ;Round if remainder.
ADC AL,0
XOR AH,AH ;Zero in high half.
MOV BX,AX ;Save directory sectors.
MUL DL ;Times entries/sector
MOV ROOT_DIRECTORY_ENTRIES,AX ; = total directory entries.
POP AX ;Retrieve total sectors.
MOV CL,1 ;Assume 1 sector/cluster.
CMP AX,THIRTY_TWO_K - 2 ;If total over half of maximum.
JB STORE_RESULTS ;If yes, assumed right.
INC CL ;Else, use 2 sectors/cluster.
STORE_RESULTS: MOV SECTORS_PER_CLUSTER,CL ;Store sectors/cluster.
SUB AX,BX ;Subtract directory sectors
DEC AX ;Subtract boot sector
SHR CL,1 ;Divide cluster size by 2.
SHR AX,CL ;Divide to get clusters.
MOV CX,AX ;Save clusters in CX.
SHL AX,1 ;Clusters * 2 = 16 bit FAT bytes.
MOV BL,0FFH ;0FFh as 3rd byte in 16 bit FAT.
CMP CX,TWELVE_BIT_FAT
JA GOT_FAT ;If clusters > 4087, 16 bit FAT.
ADD AX,CX ;Else, 1.5 bytes per 12 bit FATs.
INC AX ;Round up.
SHR AX,1
XOR BL,BL ;Use zero as 3rd byte in FAT.
GOT_FAT: XOR DX,DX ;Zero in high half.
DIV BP ;Divide by bytes/sector.
ADD DX,-1 ;Round up.
ADC AX,0
MOV SECTORS_PER_FAT,AX ;Store sectors/FAT.
CALL FORMAT ;Format the disk.
CLC ;CY = 0 for success.
ALLOCATE_END: POP DI ;Restore request header pointers.
POP ES
RET
ALLOCATE ENDP
;-----------------------------;
; INPUT - BL = 00h for 12 bit fat. BL = FFh for 16 bit fat.
;-----------------------------;
FORMAT PROC NEAR
PUSH BX ;Save BX.
MOV DX,HANDLE ;Retrieve EMM handle.
MOV CX,PAGES ;Retrieve pages.
CMP CX,3 ;Maximum for boot, FAT, and
JB NEXT_MAP_PAGE ; directory = 3 * 16K pages.
MOV CX,3
NEXT_MAP_PAGE: MOV AX,CX ;Physical page.
DEC AX ;Pages are zero based.
MOV BX,AX ;Logical page the same.
MOV AH,44H ;Map Handle Page.
INT 67H
LOOP NEXT_MAP_PAGE ;Map all necessary pages.
MOV AX,FRAME_SEGMENT ;Retrieve frame segment.
MOV ES,AX
XOR DI,DI ;Offset of zero.
MOV SI,OFFSET BOOT_RECORD ;Store boot record
MOV CX,BOOT_RECORD_LENGTH / 2 ; in sector zero.
REP MOVSW
MOV DI,BYTES_PER_SECTOR ;Retrieve bytes/sector; sector 1.
MOV AX,SECTORS_PER_FAT ;Retrieve sectors/FAT.
MUL DI ;Multiply to get total bytes.
MOV CX,AX
MOV AL,MEDIA_DESCRIPTOR ;Store media descriptor
STOSB ; as first FAT byte.
MOV AX,0FFFFH ;0FFFFh next two bytes of FAT.
STOSW
POP BX ;0 for 12 bit FAT; 0FFh for 16
MOV AL,BL ; bit FAT next byte.
STOSB
SUB CX,4 ;Adjust FAT bytes by 4 control
SHR CX,1 ; bytes; divide by two for words.
XOR AX,AX ;Format FAT with zeros.
REP STOSW
MOV SI,OFFSET VOLUME_LABEL ;DI now points to directory.
MOV CX,VOLUME_LENGTH / 2 ;Store volume label as first
REP MOVSW ; entry in directory.
MOV BX,ROOT_DIRECTORY_ENTRIES ;Retrieve directory entries.
DEC BX ;Less one for volume entry.
MOV CL,4 ;Times 32 / 2 bytes per word.
SHL BX,CL
MOV CX,BX
REP STOSW ;Zeros in directory.
MOV FORMAT_FLAG,1 ;Tell Media Check disk changed.
RET
FORMAT ENDP
;--------------------------------------------------------;
; Display disk size, sector size, and directory entries. ;
;--------------------------------------------------------;
STATISTICS: MOV SI,OFFSET DISK_SIZE_MSG
CALL TTY_STRING
MOV AX,DISK_SIZE
CALL DECIMAL_OUTPUT
MOV SI,OFFSET SECTOR_SIZE_MSG
CALL TTY_STRING
MOV AX,BYTES_PER_SECTOR
CALL DECIMAL_OUTPUT
MOV SI,OFFSET DIRECTORY_ENTRIES_MSG
CALL TTY_STRING
MOV AX,ROOT_DIRECTORY_ENTRIES
CALL DECIMAL_OUTPUT
RET
;-------------------------;
TTY: CALL WRITE_TTY
TTY_STRING: LODSB
OR AL,AL
JNZ TTY
RET
;-------------------------;
WRITE_TTY: MOV AH,0EH
INT 10H
RET
;-------------------------;
PRINT_STRING: MOV AH,9
INT 21H
RET
;----------------------------------------;
; INPUT: AX = number.
;----------------------------------------;
DECIMAL_OUTPUT PROC NEAR
MOV BX,10 ;Divisor of ten.
XOR CX,CX ;Zero in counter.
NEXT_COUNT: XOR DX,DX ;Zero in high half.
DIV BX ;Divide by ten.
ADD DL,"0" ;Convert to ASCII.
PUSH DX ;Save results.
INC CX ;Also increment count.
CMP AX,0 ;Are we done?
JNZ NEXT_COUNT ;Continue until zero.
MOV BX,CX ;Save the number of characters.
NEXT_NUMBER: POP AX ;Retrieve numbers.
CALL WRITE_TTY ;And write them.
LOOP NEXT_NUMBER
RET
DECIMAL_OUTPUT ENDP
EVEN
MAX_RESIDENT EQU $
;************* END OF RESIDENT PORTION *************;
HEADING LABEL BYTE
COPYRIGHT DB CR,LF,"XPANDISK.SYS 1.0 (C) 1988 Ziff Communications Co."
PROGRAMMER DB CR,LF,"PC Magazine ",BOX," Michael J. Mefford",CR,LF,LF
DB "Syntax: XPANDISK.SYS [/D disk size][/S sector size][/E entries][/M][/A]"
DB CR,LF,LF
DB "disk size = (16 - 32768)K bytes; default = 64",CR,LF
DB "sector size = (128,256,512) bytes; default = 256",CR,LF
DB "entries = (4 - 512)in root directory; default = 64",CR,LF
DB "/M = Minimum disk size (16K)",CR,LF
DB "/A = All of available expanded memory",CR,LF,LF
DB "Use XPANBOSS.COM to control installed XPANDISK",CR,LF,"$"
EMM DB "EMMXXXX0"
EMM_LENGTH EQU $ - EMM
EMM_NOT_FOUND DB CR,LF,"Expanded memory driver not found",CR,LF,LF,"$"
INSTALLED DB CR,LF,LF,"XPANDISK installed",CR,LF,LF,"$"
;------------------------------------------;
; INPUT - ES:DI points to request header.
; OUTPUT - AX = Error status
;------------------------------------------;
INIT PROC NEAR
MOV DX,OFFSET HEADING ;Display copyright.
CALL PRINT_STRING
PUSH DS ;Save segment registers.
PUSH ES
MOV SI,ES:ARGUMENTS_OR_ARRAY_OFF[DI] ;Point to CONFIG.SYS
MOV DS,ES:ARGUMENTS_OR_ARRAY_SEG[DI] ; XPANDISK parameters.
PARSE_LEADING: LODSB ;Parse of any leading
CMP AL,SPACE ; white space after DEVICE =.
JBE PARSE_LEADING
FIND_END: LODSB ;Find end of "XPANDISK.SYS".
CMP AL,SPACE
JA FIND_END
DEC SI
CALL PARSE ;Parse parameters.
POP ES ;Restore segment registers.
POP DS
MOV ES:UNITS[DI],1 ;Set up header.
MOV ES:ARGUMENTS_OR_ARRAY_OFF[DI],OFFSET BPB_ARRAY
MOV ES:ARGUMENTS_OR_ARRAY_SEG[DI],CS
PUSH ES ;Save request header pointers.
PUSH DI
MOV AX,3567H ;Retrieve EMM interrupt vector.
INT 21H
MOV DI,0AH ;See if offset 10 of vector
MOV SI,OFFSET EMM ; points to EMMXXXX0.
MOV CX,EMM_LENGTH
REPZ CMPSB
POP DI ;Restore segments.
POP ES
JNZ NO_EMM ;Exit if EMM not found.
MOV AH,40H ;Get Status of EMM.
INT 67H
OR AH,AH
JNZ NO_EMM ;Exit if not working.
MOV AH,41H ;Get Page Frame Address.
INT 67H
OR AH,AH ;Exit if not available.
JNZ NO_EMM
MOV FRAME_SEGMENT,BX ;Else, store.
CALL ALLOCATE ;Allocate memory and format disk.
JNC FOUND_EMM ;Exit with error if failed.
NO_EMM: MOV ES:ENDING_OFFSET[DI],OFFSET MIN_RESIDENT
MOV ES:ENDING_SEGMENT[DI],CS
MOV ES:UNITS[DI],0 ;Install zero units.
MOV EMM_FLAG,0 ;Fail any other driver calls.
MOV DX,OFFSET EMM_NOT_FOUND ;Tell user not installed.
JMP SHORT INIT_END
FOUND_EMM: MOV ES:ENDING_OFFSET[DI],OFFSET MAX_RESIDENT
MOV ES:ENDING_SEGMENT[DI],CS
CALL STATISTICS ;If successful installation
MOV DX,OFFSET INSTALLED ; display stats and "installed".
INIT_END: CALL PRINT_STRING
XOR AX,AX
RET
INIT ENDP
_TEXT ENDS
END