Category : Utilities for DOS and Windows Machines
Archive   : XPANDISK.ZIP
Filename : XPANDISK.ASM

 
Output of file : XPANDISK.ASM contained in archive : XPANDISK.ZIP
; 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


  3 Responses to “Category : Utilities for DOS and Windows Machines
Archive   : XPANDISK.ZIP
Filename : XPANDISK.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/