forked from ChartreuseK/FREESP
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFREESP.ASM
More file actions
300 lines (279 loc) · 8.43 KB
/
FREESP.ASM
File metadata and controls
300 lines (279 loc) · 8.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
; FREESP.ASM -- Chartreuse 2021
;
; Assembles with TASM 2.01 and TLINK 2.0, should work on other versions
; TASM FREESP.ASM
; TLINK /T FREESP.OBJ
;
; Calculate free space on FAT16 disk as fast as possible
; Read in the partition information to get location of
; FAT. Read in FAT, scan for non-free (!= 0000) clusters,
; calculate total clusters in volume, and subtract to get
; the number of free clusters.
; Finally store the value in the MSDOS DPB for the volume
;------------------------------------------------------------
.model tiny
.code
LOCALS @@
ORG 100h
start:
lea sp, stacktop
; Check if we have enough memory to load the entire 128kB
; FAT at once
mov bx, WORD PTR [ds:02h] ; Read in segment after our allocation
mov ax, cs
sub bx, ax ; # of segments we've been allocated
cmp bx, ((64+2)*64) ; We need at least 64kB + ~2kB for prog
jb @@nomem
; Compute segment for the buffer
add ax, 2*64 ; Allocate 2kB for our main program and data
; Giving us the segment pointer for our 64k
; buffer
mov WORD PTR [fatseg], ax ; Save segment
jmp @@ahead
@@nomem:
mov ah,09h ; Insufficient memory error, abort
lea dx, insufmem
int 21h
int 20h
@@unknowndisk:
mov ah, 09h ; Filesystem doesn't match expectations
lea dx, diskerr ; abort
int 21h
int 20h
@@usage:
mov ah, 09h ; Invalid parameter
lea dx, usagestr
int 21h
int 20h
@@ahead:
mov al, BYTE PTR [ds:81h] ; Check if we were run with no params
cmp al, 0Dh
je @@usage
mov al, BYTE PTR [ds:82h] ; First character of parameter
and al, 0DFh ; Convert to uppercase
cmp al, 'A'
jb @@usage
cmp al, 'Z'
ja @@usage
sub al, 'A' ; Convert to drive number
mov BYTE PTR [drv], al
;---------------------------------
; Read in boot sector for partition
; Setup abs disk struct
lea bx, absdisk
mov WORD PTR [bx+0], 0 ; Logical sector (low)
mov WORD PTR [bx+2], 0 ; Logical sector (high)
mov WORD PTR [bx+4], 1 ; # of sectors to read
mov WORD PTR [bx+6], OFFSET bootseg
mov ax, ds
mov WORD PTR [bx+8], ax ; Address (FAR) to read to
mov al, BYTE PTR [drv] ; Drive #
mov cx, 0FFFFh ; Large disk, use AbsDiskIORec Struct
mov dx, 0 ; Beginning sector
int 25h ; Absolute disk read
pop dx ; Remove leftover word from stack from int25h
jnc @@ahead2
jmp readfail
@@ahead2:
; We've now got the partition boot sector
; Figure out which sector the FAT is at
lea si, bootseg
cmp WORD PTR [si+0Bh], 512 ; Make sure this is 512 bytes/sector
jne @@unknowndisk
; Check that we're FAT16 by looking at the cluster count
push si
call getTotalClusters
add sp, 2
and dx, dx
jne @@unknowndisk ; Total clusters >= 65536 = FAT32
cmp ax, 65524
ja @@unknowndisk ; >= 65525 = FAT32
cmp ax, 4085
jb @@unknowndisk ; < 4085 = FAT12
; Assume we're looking at a FAT16 filesystem now
mov ax, WORD PTR [si+16h] ; # of sectors per FAT
cmp ax, 100h ; More than 65536 clusters, not FAT16
ja @@unknowndisk
; int 25h will not read more than 64kB (filling the segment
; of the far pointer passed). If # clusters is > 128 (128*512=64kB)
; then we need to do multiple reads. And for any decently sized
; disk it will be 256. (2x 64kB reads)
mov bx, WORD PTR [si+0Eh] ; start of FAT
cmp ax, 128
jbe @@second ; If <=128 total then no need to do 2 reads
push bx ; preserve start sector
mov ax, 128
push ax ; sectcount
push bx ; startsect
call getUsedClust ; getUsedClust(resvsect, 128)
add sp, 4
add WORD PTR [usedclust], ax ; # of used custers so far
pop bx ; restort start sector
add bx, 128 ; start sector for remaining
mov ax, WORD PTR [si+16h]
sub ax, 128 ; sectcount for remaining
@@second:
push ax ; sectcount
push bx ; startsect
call getUsedClust
add sp, 4
add WORD PTR [usedclust], ax ; # of used clusters
sub WORD PTR [usedclust], 2 ; First two entries are used
; to store the FAT signature
; Don't count them.
; We've got the number of used clusters
; Get total clusters
; Can't use int21h:1Ch since that runs the slow drive space
; calculator for some reason
push si
call getTotalClusters
add sp, 2
; Since we're FAT16, ax contains the total clusters
sub ax, WORD PTR [usedclust] ; AX = available clusters
push ds
push ax
; Now we change the dos data structure
mov ah, 32h ; Get Drive Parameter Block
mov dl, BYTE PTR [drv] ; Get drive ID
inc dl ; Drive # is offset by 1 compared with 25h
int 21h
; DS:BX now points straight to the appropriate data structure
; For DOS 4-6 offset 1F contains a word with the # of free clusters
; on drive, with FFFFh if unknown
pop ax
mov WORD PTR [bx+1Fh], ax ; Update free clusters
pop ds
int 20h ; Exit
;-------------------------------------
readfail:
mov ah, 09h
lea dx, readfailstr
int 21h
int 20h
;-------------------------
; long getTotalClusters(void *bootsect)
; Calculates total clusters on FAT volume
; DS:AX total clusters
getTotalClusters PROC
ARG bootsect:WORD
push bp
mov bp, sp
push si
mov si, [bootsect]
; Total clusters =
; total sectors - hidden sectors - num fats * sectors per fat
; -------------------------------------------------------------
; sectors per cluster
; This will involve 32-bit maths
xor dx,dx
mov ax, WORD PTR [si+13h] ; Number of sectors (small)
and ax,ax
jne @@small ; If not 0 then word sized # of sectors
; (Why are you using this tool for that...)
mov ax, WORD PTR [si+20h] ; Number of sectors (large-low word)
mov dx, WORD PTR [si+22h] ; (high word)
@@small:
; DX:AX contains 32-bit total number of sectors
mov bx, WORD PTR [si+1Ch] ; # of hidden sectors
sub ax, bx ; Subtract from total
sbb dx, 0
mov cl, BYTE PTR [si+10h] ; # of FATs
xor ch, ch
mov bx, WORD PTR [si+16h] ; Sectors per FAT
@@fats:
sub ax, bx ; sectors per FAT
sbb dx, 0
loop @@fats
; DX:AX is now the number of available sectors
; Convert to clusters
mov bl, BYTE PTR [si+0Dh] ; Sectors per cluster
; Guaranteed to be power of 2
@@sectclust:
shr bl, 1
jc @@found ; Found our power of 2 stop dividing
shr dx, 1
rcr ax, 1 ; Divide DX:AX by 2
jmp @@sectclust
@@found:
; DX:AX now contains available clusters. Since FAT16 must be less
; than 65525 clusters our result is in AX
pop si
pop bp
ret
getTotalClusters ENDP
;------------------------------------------------
; word getUsedClust(word startsect, byte sectcount)
; startsect must be less than 65536 (fit in a word)
; (FAT should be in that area)
; sectcount must be 128 or less
getUsedClust PROC
ARG startsect:WORD, sectcount:BYTE
push bp
mov bp, sp
push di
push si
lea bx, absdisk
mov ax, [startsect]
mov WORD PTR [bx+0], ax ; Start sector (low word)
xor ax, ax
mov WORD PTR [bx+2], ax ; (high word)
mov al, [sectcount]
mov WORD PTR [bx+4], ax ; # of sectors to read
mov WORD PTR [bx+6], 0 ; offset of address to load to
mov ax, WORD PTR [fatseg]
mov WORD PTR [bx+8], ax ; Segment of address to load to
mov al, BYTE PTR [drv] ; Drive #
mov cx, 0FFFFh ; Use AbsDiskIORec
mov dx, 0 ; Beginning sector
int 25h
pop dx ; Fix stack
jc readfail
; Turn # sectors into number of entries to check
xor cl, cl
mov ch, [sectcount] ; sectcount * 256 (words per sector)
mov ax, WORD PTR [fatseg]
mov es, ax
xor di, di
xor bx, bx ; # of non-free sectors
xor ax, ax ; 0 indicates free sector
@@scan:
repe scasw ; Scan till we hit a non-free sector
; We've hit a non-free sector, or are out of sectors to check
jcxz @@done ; Done checking all entries
inc bx ; Sector must not be free, count and
jmp @@scan ; contiue
@@done:
je @@nofix ; If NE then we didn't count the last used
inc bx ; entry. Fix off by 1 error
@@nofix:
mov ax, bx ; # of non-free sectors
pop si
pop di
pop bp
ret
getUsedClust ENDP
.data
usedclust:
DW 0 ; # of non-free clusters
fatseg: DW 0 ; Segment to contain our 128kB buffer
drv: DB 0 ; Drive #
insufmem:
DB "Insufficient memory.",10,13,"$"
diskerr:
DB "Unknown disk type.",10,13,"$"
endstr: DB " Bytes available.",10,13,"$"
usagestr:
DB "usage: FREESP <A-Z>",10,13
DB " Precalculate free space on FAT16 filesystem fast.",10,13
DB " Version 0.1",10,13,"$"
readfailstr:
DB "Error reading from disk.",10,13,"$"
absdisk:
DB 10 dup(?) ; AbsDiskIORec struct
bootseg:
DB 512 dup(?)
; Stack
DW 32 dup(?)
stacktop:
end start