Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3232 | Serge | 1 | ; FAT-specific code for tmpdisk.asm. |
2 | ; Formats a disk to FAT16 or FAT32, depending on size. |
||
3 | ; Note: formatting is adjusted for memory-based disks. Although the resulting |
||
4 | ; image is a valid FAT32 volume, it has no "spare" sectors, e.g. second copy |
||
5 | ; of FAT or place for second sector of MS FAT32 bootloader. |
||
6 | |||
7 | ; Some constants |
||
8 | FAT16_ROOTDIR_SECTORS = 16 ; can be changed, but why not? |
||
9 | ; FAT16: |
||
10 | ; 1 bootsector, |
||
11 | ; min 0xFF5 sectors for data, |
||
12 | ; min (0xFF5*2/512) = 16 sectors per FAT, we use only one copy, |
||
13 | ; FAT16_ROOTDIR_SECTORS for root directory |
||
14 | MIN_FAT16_SIZE = 1 + 16 + FAT16_ROOTDIR_SECTORS + 0xFF5 |
||
15 | ; FAT32: |
||
16 | ; 1 bootsector, |
||
17 | ; 1 sector for fsinfo, |
||
18 | ; min 0xFFF5 sectors for data, |
||
19 | ; min (0xFFF5*4/512) = 512 sectors per FAT, we use only one copy |
||
20 | MIN_FAT32_SIZE = 1 + 1 + 512 + 0xFFF5 |
||
21 | MAX_SIZE = 1 shl (30 - 9) ; 1G in 512-byte sectors |
||
22 | |||
23 | ; Initializes FATxx structures on the disk. |
||
24 | ; Called with edi = pointer to disk data, esi = size of disk. |
||
25 | proc format_disk |
||
26 | ; Determine FAT type and jump to the corresponding handler. |
||
27 | cmp esi, MIN_FAT32_SIZE |
||
28 | jae format_disk_fat32 |
||
29 | ; Fall through to format_disk_fat16. |
||
30 | endp |
||
31 | |||
32 | ; Structure of FAT16 bootsector. Field names are from MS spec. |
||
33 | struc FAT16BOOT |
||
34 | { |
||
35 | .BS_jmpBoot rb 3 |
||
36 | .BS_OEMName rb 8 |
||
37 | .BPB_BytsPerSec dw ? |
||
38 | .BPB_SecsPerClus db ? |
||
39 | .BPB_RsvdSecCnt dw ? |
||
40 | .BPB_NumFATs db ? |
||
41 | .BPB_RootEntCnt dw ? |
||
42 | .BPB_TotSec16 dw ? |
||
43 | .BPB_Media db ? |
||
44 | .BPB_FATSz16 dw ? |
||
45 | .BPB_SecPerTrk dw ? |
||
46 | .BPB_NumHeads dw ? |
||
47 | .BPB_HiddSec dd ? |
||
48 | .BPB_TotSec32 dd ? |
||
49 | .BS_DrvNum db ? |
||
50 | .BS_Reserved1 db ? |
||
51 | .BS_BootSig db ? |
||
52 | .BS_VolID dd ? |
||
53 | .BS_VolLab rb 11 |
||
54 | .BS_FilSysType rb 8 |
||
55 | } |
||
56 | virtual at 0 |
||
57 | FAT16BOOT FAT16BOOT |
||
58 | end virtual |
||
59 | |||
60 | ; Initializes FAT16 structures on the disk. |
||
61 | ; Called with edi = pointer to disk data, esi = size of disk. |
||
62 | format_disk_fat16: |
||
63 | ; 1. Calculate number of clusters. |
||
64 | ; 1a. There are fixed-sized areas and there are data+FAT; |
||
65 | ; every cluster uses 512 bytes in data area and 2 bytes in FAT area. |
||
66 | lea eax, [esi-1-FAT16_ROOTDIR_SECTORS] |
||
67 | ; two following lines are equivalent to edx = floor(eax*512/514) |
||
68 | mov ecx, 0xFF00FF01 |
||
69 | mul ecx ; edx = number of clusters |
||
70 | ; 1b. Force the number be less than 0xfff5. |
||
71 | mov eax, 0xFFF4 |
||
72 | cmp edx, eax |
||
73 | jb @f |
||
74 | mov edx, eax |
||
75 | @@: |
||
76 | ; 2. Zero all system areas on the disk. |
||
77 | lea ecx, [256*(1+FAT16_ROOTDIR_SECTORS)/2+edx+255] |
||
78 | and ecx, not 255 |
||
79 | shr ecx, 1 |
||
80 | xor eax, eax |
||
81 | push edi |
||
82 | rep stosd |
||
83 | pop edi |
||
84 | ; 3. Generate the bootsector. |
||
85 | ; 3a. Copy static stub. |
||
86 | push esi edi |
||
87 | mov esi, fat16bootsector_stub |
||
88 | mov ecx, fat16bootsector_stub_size |
||
89 | rep movsb |
||
90 | pop edi esi |
||
91 | mov word [edi+510], 0xAA55 |
||
92 | ; 3b. Set fields which depend on size. |
||
93 | cmp esi, 0x10000 |
||
94 | jae .size_is_32bit |
||
95 | mov [edi+FAT16BOOT.BPB_TotSec16], si |
||
96 | jmp .size_written |
||
97 | .size_is_32bit: |
||
98 | mov [edi+FAT16BOOT.BPB_TotSec32], esi |
||
99 | .size_written: |
||
100 | lea eax, [edx+255] |
||
101 | shr eax, 8 |
||
102 | mov [edi+FAT16BOOT.BPB_FATSz16], ax |
||
103 | ; 3c. Generate volume ID. |
||
104 | call generate_volume_id |
||
105 | mov [edi+FAT16BOOT.BS_VolID], eax |
||
106 | ; 4. Initialize FAT. |
||
107 | mov dword [edi+512], 0xFFFFFFF8 |
||
108 | ; 5. Return. |
||
109 | ret |
||
110 | |||
111 | ; Structure of FAT32 bootsector. Field names are from MS spec. |
||
112 | struc FAT32BOOT |
||
113 | { |
||
114 | .BS_jmpBoot rb 3 |
||
115 | .BS_OEMName rb 8 |
||
116 | .BPB_BytsPerSec dw ? |
||
117 | .BPB_SecsPerClus db ? |
||
118 | .BPB_RsvdSecCnt dw ? |
||
119 | .BPB_NumFATs db ? |
||
120 | .BPB_RootEntCnt dw ? |
||
121 | .BPB_TotSec16 dw ? |
||
122 | .BPB_Media db ? |
||
123 | .BPB_FATSz16 dw ? |
||
124 | .BPB_SecPerTrk dw ? |
||
125 | .BPB_NumHeads dw ? |
||
126 | .BPB_HiddSec dd ? |
||
127 | .BPB_TotSec32 dd ? |
||
128 | .BPB_FATSz32 dd ? |
||
129 | .BPB_ExtFlags dw ? |
||
130 | .BPB_FSVer dw ? |
||
131 | .BPB_RootClus dd ? |
||
132 | .BPB_FSInfo dw ? |
||
133 | .BPB_BkBootSec dw ? |
||
134 | .BPB_Reserved rb 12 |
||
135 | .BS_DrvNum db ? |
||
136 | .BS_Reserved1 db ? |
||
137 | .BS_BootSig db ? |
||
138 | .BS_VolID dd ? |
||
139 | .BS_VolLab rb 11 |
||
140 | .BS_FilSysType rb 8 |
||
141 | } |
||
142 | virtual at 0 |
||
143 | FAT32BOOT FAT32BOOT |
||
144 | end virtual |
||
145 | |||
146 | ; Initializes FAT32 structures on the disk. |
||
147 | ; Called with edi = pointer to disk data, esi = size of disk. |
||
148 | format_disk_fat32: |
||
149 | ; 1. Calculate number of clusters. |
||
150 | ; 1a. There is fixed-sized area and there are data+FAT; |
||
151 | ; every cluster uses 512 bytes in data area and 4 bytes in FAT area. |
||
152 | lea eax, [esi-1-1] |
||
153 | ; two following lines are equivalent to edx=floor(eax*512/516) if eax<10000000h |
||
154 | mov ecx, 0xFE03F810 |
||
155 | mul ecx ; edx = number of clusters |
||
156 | ; 2. Zero all system areas on the disk and first cluster of data, |
||
157 | ; used for root directory. |
||
158 | lea ecx, [128*(1+1+1)+edx+127] |
||
159 | and ecx, not 127 |
||
160 | xor eax, eax |
||
161 | push edi |
||
162 | rep stosd |
||
163 | pop edi |
||
164 | ; 3. Generate the bootsector. |
||
165 | ; 3a. Copy static stub. |
||
166 | push esi edi |
||
167 | mov esi, fat32bootsector_stub |
||
168 | mov ecx, fat32bootsector_stub_size |
||
169 | rep movsb |
||
170 | pop edi esi |
||
171 | mov word [edi+510], 0xAA55 |
||
172 | ; 3b. Set fields which depend on size. |
||
173 | mov [edi+FAT32BOOT.BPB_TotSec32], esi |
||
174 | lea eax, [edx+127] |
||
175 | shr eax, 7 |
||
176 | mov [edi+FAT32BOOT.BPB_FATSz32], eax |
||
177 | ; 3c. Generate volume ID. |
||
178 | call generate_volume_id |
||
179 | mov [edi+FAT32BOOT.BS_VolID], eax |
||
180 | ; 4. Initialize fsinfo sector. |
||
181 | mov dword [edi+512], 'RRaA' |
||
182 | mov dword [edi+512+484], 'rrAa' |
||
183 | dec edx ; one cluster is occupied by root dir |
||
184 | mov dword [edi+512+488], edx ; free count |
||
185 | mov byte [edi+512+492], 3 ; first free cluster |
||
186 | mov word [edi+512+510], 0xAA55 |
||
187 | ; 5. Initialize FAT. |
||
188 | mov dword [edi+512*2], 0x0FFFFFF8 |
||
189 | mov dword [edi+512*2+4], 0x0FFFFFFF |
||
190 | mov dword [edi+512*2+8], 0x0FFFFFFF |
||
191 | ; 6. Return. |
||
192 | ret |
||
193 | |||
194 | ; Generate volume serial number, which should try to be unique for each volume. |
||
195 | ; Use CMOS date+time, copy-pasted from fat32.inc. |
||
196 | generate_volume_id: |
||
197 | call get_time_for_file |
||
198 | mov cx, ax |
||
199 | call get_date_for_file |
||
200 | shl eax, 16 |
||
201 | mov ax, cx |
||
202 | ret |
||
203 | |||
204 | ; Three following procedures are copy-pasted from fat32.inc. |
||
205 | bcd2bin: |
||
206 | ;---------------------------------- |
||
207 | ; input : AL=BCD number (eg. 0x11) |
||
208 | ; output : AH=0 |
||
209 | ; AL=decimal number (eg. 11) |
||
210 | ;---------------------------------- |
||
211 | xor ah, ah |
||
212 | shl ax, 4 |
||
213 | shr al, 4 |
||
214 | aad |
||
215 | ret |
||
216 | |||
217 | get_date_for_file: |
||
218 | ;----------------------------------------------------- |
||
219 | ; Get date from CMOS and pack day,month,year in AX |
||
220 | ; DATE bits 0..4 : day of month 0..31 |
||
221 | ; 5..8 : month of year 1..12 |
||
222 | ; 9..15 : count of years from 1980 |
||
223 | ;----------------------------------------------------- |
||
224 | mov al, 0x7 ;day |
||
225 | out 0x70, al |
||
226 | in al, 0x71 |
||
227 | call bcd2bin |
||
228 | ror eax, 5 |
||
229 | |||
230 | mov al, 0x8 ;month |
||
231 | out 0x70, al |
||
232 | in al, 0x71 |
||
233 | call bcd2bin |
||
234 | ror eax, 4 |
||
235 | |||
236 | mov al, 0x9 ;year |
||
237 | out 0x70, al |
||
238 | in al, 0x71 |
||
239 | call bcd2bin |
||
240 | add ax, 20 ;because CMOS return only the two last |
||
241 | ;digit (eg. 2000 -> 00 , 2001 -> 01) and we |
||
242 | rol eax, 9 ;need the difference with 1980 (eg. 2001-1980) |
||
243 | ret |
||
244 | |||
245 | |||
246 | get_time_for_file: |
||
247 | ;----------------------------------------------------- |
||
248 | ; Get time from CMOS and pack hour,minute,second in AX |
||
249 | ; TIME bits 0..4 : second (the low bit is lost) |
||
250 | ; 5..10 : minute 0..59 |
||
251 | ; 11..15 : hour 0..23 |
||
252 | ;----------------------------------------------------- |
||
253 | mov al, 0x0 ;second |
||
254 | out 0x70, al |
||
255 | in al, 0x71 |
||
256 | call bcd2bin |
||
257 | ror eax, 6 |
||
258 | |||
259 | mov al, 0x2 ;minute |
||
260 | out 0x70, al |
||
261 | in al, 0x71 |
||
262 | call bcd2bin |
||
263 | ror eax, 6 |
||
264 | |||
265 | mov al, 0x4 ;hour |
||
266 | out 0x70, al |
||
267 | in al, 0x71 |
||
268 | call bcd2bin |
||
269 | rol eax, 11 |
||
270 | ret |
||
271 | |||
272 | ; some data |
||
273 | fat16bootsector_stub: |
||
274 | db 0EBh, 3Ch, 90h ; BS_jmpBoot |
||
275 | db 'KOLIBRI ' ; BS_OEMName |
||
276 | dw 512 ; BPB_BytsPerSec |
||
277 | db 1 ; BPB_SecsPerClus |
||
278 | dw 1 ; BPB_RsvdSecCnt |
||
279 | db 1 ; BPB_NumFATs |
||
280 | dw FAT16_ROOTDIR_SECTORS*16 ; BPB_RootEntCnt |
||
281 | dw 0 ; BPB_TotSec16, filled in format_disk_fat16 |
||
282 | db 0F8h ; BPB_Media |
||
283 | dw 0 ; BPB_FATSz16, filled in format_disk_fat16 |
||
284 | dw 32 ; BPB_SecPerTrk |
||
285 | dw 128 ; BPB_NumHeads |
||
286 | dd 0 ; BPB_HiddSec |
||
287 | dd 0 ; BPB_TotSec32, filled in format_disk_fat16 |
||
288 | db 80h ; BS_DrvNum |
||
289 | db 0 ; BS_Reserved1 |
||
290 | db 29h ; BS_BootSig |
||
291 | dd 0 ; BS_VolID, filled in format_disk_fat16 |
||
292 | db 'NO NAME ' ; BS_VolLab |
||
293 | db 'FAT16 ' ; BS_FilSysType |
||
294 | ; just in case add some meaningful bytes if someone tries to boot |
||
295 | db 0CDh, 19h, 0EBh, 0FEh ; int 19h, jmp $ |
||
296 | fat16bootsector_stub_size = $ - fat16bootsector_stub |
||
297 | fat32bootsector_stub: |
||
298 | db 0EBh, 58h, 90h ; BS_jmpBoot |
||
299 | db 'KOLIBRI ' ; BS_OEMName |
||
300 | dw 512 ; BPB_BytsPerSec |
||
301 | db 1 ; BPB_SecsPerClus |
||
302 | dw 1 ; BPB_RsvdSecCnt |
||
303 | db 1 ; BPB_NumFATs |
||
304 | dw 0 ; BPB_RootEntCnt |
||
305 | dw 0 ; BPB_TotSec16 |
||
306 | db 0F8h ; BPB_Media |
||
307 | dw 0 ; BPB_FATSz16 |
||
308 | dw 32 ; BPB_SecPerTrk |
||
309 | dw 128 ; BPB_NumHeads |
||
310 | dd 0 ; BPB_HiddSec |
||
311 | dd 0 ; BPB_TotSec32, filled in format_disk_fat32 |
||
312 | dd 0 ; BPB_FATSz32, filled in format_disk_fat32 |
||
313 | dw 0 ; BPB_ExtFlags |
||
314 | dw 0 ; BPB_FSVer |
||
315 | dd 2 ; BPB_RootClus |
||
316 | dw 1 ; BPB_FSInfo |
||
317 | dw 0 ; BPB_BkBootSec |
||
318 | rb 12 ; BPB_Reserved |
||
319 | db 80h ; BS_DrvNum |
||
320 | db 0 ; BS_Reserved1 |
||
321 | db 29h ; BS_BootSig |
||
322 | dd 0 ; BS_VolID, filled in format_disk_fat32 |
||
323 | db 'NO NAME ' ; BS_VolLab |
||
324 | db 'FAT32 ' ; BS_FilSysType |
||
325 | ; same bytes as in fat16bootsector_stub |
||
326 | db 0CDh, 19h, 0EBh, 0FEh ; int 19h, jmp $ |
||
327 | fat32bootsector_stub_size = $ - fat32bootsector_stub10000000h |