207 lines
5.7 KiB
NASM
207 lines
5.7 KiB
NASM
name free
|
|
page 60,132
|
|
title 'FREE --- Report free space on disk'
|
|
|
|
; FREE --- a utility to report free space on
|
|
; the default or selected disk drive.
|
|
;
|
|
; Requires PC-DOS or MS-DOS 2.0.
|
|
;
|
|
; Used in the form:
|
|
; A> FREE [unit:]
|
|
; (item in square brackets is optional)
|
|
;
|
|
|
|
cr equ 0dh ;ASCII carriage return
|
|
lf equ 0ah ;ASCII line feed
|
|
blank equ 20h ;ASCII space code
|
|
eom equ '$' ;end of string marker
|
|
|
|
|
|
; Here we define a dummy segment containing labels
|
|
; for the default file control block and the command tail buffer,
|
|
; so that the main program can access those locations.
|
|
;
|
|
psp segment para public 'PSP'
|
|
|
|
org 05ch
|
|
fcb label byte ;default file control block
|
|
|
|
org 080h
|
|
command label byte ;default command buffer
|
|
|
|
psp ends
|
|
|
|
|
|
cseg segment para public 'CODE'
|
|
|
|
assume cs:cseg,ds:psp,es:data,ss:stack
|
|
|
|
|
|
get_drive proc near ;get drive selection, if any,
|
|
;otherwise obtain the identity
|
|
;of the current disk drive.
|
|
;Return drive (1=A, 2=B, etc) in AL.
|
|
;
|
|
mov al,fcb ;Pick up the drive code, parsed
|
|
;by DOS into the default file
|
|
;control block.
|
|
or al,al ;Is it the default?
|
|
jnz get_drive1 ;no, use it
|
|
mov ah,19h ;Yes, get the actual current
|
|
int 21h ;drive from PC-DOS.
|
|
inc al ;Increment to match FCB code.
|
|
get_drive1: ;Return drive code in AL.
|
|
ret
|
|
get_drive endp
|
|
|
|
|
|
free proc far ;entry point from PC-DOS
|
|
|
|
push ds ;save DS:0000 for final
|
|
xor ax,ax ;return to PC-DOS
|
|
push ax
|
|
mov ax,data ;make our data segment
|
|
mov es,ax ;addressable via ES register.
|
|
mov ah,30h ;check version of PC-DOS.
|
|
int 21h
|
|
cmp al,2
|
|
jae free1 ;proceed, DOS 2.0 or greater.
|
|
mov dx,offset msg2 ;DOS 1.x --- print error message
|
|
mov ax,es ;and exit. First fix up DS register
|
|
mov ds,ax ;so error message is addressable.
|
|
jmp free4
|
|
|
|
free1: call get_drive ;get drive selection into DL.
|
|
push es ;copy ES to DS for remainder
|
|
pop ds ;of the program...
|
|
assume ds:data ;and tell assembler about it.
|
|
mov dl,al
|
|
add al,'A'-1 ;form drive letter from drive code,
|
|
mov outputb,al ;and put it into the output string.
|
|
mov ah,36h ;now call DOS to get free disk space.
|
|
int 21h
|
|
cmp ax,-1 ;was drive invalid?
|
|
je free3 ;yes,go print error message
|
|
;drive was ok, so now registers are...
|
|
;AX=number of sectors per cluster
|
|
;BX=available clusters,
|
|
;CX=number of bytes per sector,
|
|
;DX=total clusters per drive.
|
|
;calculate free space:
|
|
mul cx ;sectors per cluster * bytes per sector
|
|
;(we assume this won't overflow into DX)
|
|
mul bx ;then * available clusters
|
|
|
|
;DX:AX now contains free space in bytes.
|
|
;SI = last byte address for converted string.
|
|
mov si,offset (outputa+9)
|
|
mov cx,10 ;CX = 10, radix for conversion
|
|
call bin_to_asc ;convert free space value to ASCII,
|
|
mov dx,offset output
|
|
jmp free4 ;and print it out.
|
|
|
|
free3: mov dx,offset msg1 ;illegal drive, print error
|
|
|
|
free4: mov ah,9 ;print the string whose address
|
|
int 21h ;is in DX.
|
|
ret ;then return to DOS.
|
|
|
|
free endp
|
|
|
|
|
|
; Convert 32 bit binary value to ASCII string.
|
|
;
|
|
; Call with DX:AX = signed 32 bit value
|
|
; CX = radix
|
|
; SI = last byte of area to store resulting string
|
|
; (make sure enough room is available to store
|
|
; the string in the radix you have selected.)
|
|
;
|
|
; Destroys AX, BX, CX, DX, and SI.
|
|
;
|
|
bin_to_asc proc near ;convert DX:AX to ASCII.
|
|
;force storage of at least 1 digit.
|
|
mov byte ptr [si],'0'
|
|
or dx,dx ;test sign of 32 bit value,
|
|
pushf ;and save sign on stack.
|
|
jns bin1 ;jump if it was positive.
|
|
not dx ;it was negative, take 2's complement
|
|
not ax ;of the value.
|
|
add ax,1
|
|
adc dx,0
|
|
bin1: ;divide the 32 bit value by the radix
|
|
;to extract the next digit for the
|
|
;forming string.
|
|
mov bx,ax ;is the value zero yet?
|
|
or bx,dx
|
|
jz bin3 ;yes, we are done converting.
|
|
call divide ;no, divide by radix.
|
|
add bl,'0' ;convert the remainder to an ASCII digit.
|
|
cmp bl,'9' ;we might be converting to hex ASCII,
|
|
jle bin2 ;jump if in range 0-9,
|
|
add bl,'A'-'9'-1 ;correct it if in range A-F.
|
|
bin2: mov [si],bl ;store this character into string.
|
|
dec si ;back up through string,
|
|
jmp bin1 ;and do it again.
|
|
bin3: ;restore sign flag,
|
|
popf ;was original value negative?
|
|
jns bin4 ;no, jump
|
|
;yes,store sign into output string.
|
|
mov byte ptr [si],'-'
|
|
bin4: ret ;back to caller.
|
|
bin_to_asc endp
|
|
|
|
|
|
; General purpose 32 bit by 16 bit unsigned divide.
|
|
; This must be used instead of the plain machine unsigned divide
|
|
; for cases where the quotient may overflow 16 bits (for example,
|
|
; dividing 100,000 by 2). If called with a zero divisor, this
|
|
; routine returns the dividend unchanged and gives no warning.
|
|
;
|
|
; Call with DX:AX = 32 bit dividend
|
|
; CX = divisor
|
|
;
|
|
; Returns DX:AX = quotient
|
|
; BX = remainder
|
|
; CX = divisor (unchanged)
|
|
;
|
|
divide proc near ; Divide DX:AX by CX
|
|
jcxz div1 ; exit if divide by zero
|
|
push ax ; 0:dividend_upper/divisor
|
|
mov ax,dx
|
|
xor dx,dx
|
|
div cx
|
|
mov bx,ax ; BX = quotient1
|
|
pop ax ; remainder1:dividend_lower/divisor
|
|
div cx
|
|
xchg bx,dx ; DX:AX = quotient1:quotient2
|
|
div1: ret ; BX = remainder2
|
|
divide endp
|
|
|
|
cseg ends
|
|
|
|
|
|
data segment para public 'DATA'
|
|
|
|
output db cr,lf
|
|
outputa db 10 dup (blank)
|
|
db ' bytes free on drive '
|
|
outputb db 'x:',cr,lf,eom
|
|
|
|
msg1 db cr,lf
|
|
db 'That disk drive does not exist.'
|
|
db cr,lf,eom
|
|
|
|
msg2 db cr,lf
|
|
db 'Requires DOS version 2 or greater.'
|
|
db cr,lf,eom
|
|
|
|
data ends
|
|
|
|
|
|
stack segment para stack 'STACK'
|
|
db 64 dup (?)
|
|
stack ends
|
|
|
|
end free |