· 10 years ago · Sep 01, 2016, 12:12 AM
1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2;; FAT32 driver. ;;
3;; ;;
4;; This file needs memory.asm to assemble (described below). ;;
5;; Also it needs SD_read, write, etc functions from sd.asm. ;;
6;; It assumes SD_init has been called. ;;
7;; ;;
8;; Due to simplicity of the driver, if a lot of directory ;;
9;; entries are created, the clusters allocated don't get ;;
10;; released automatically. Windows 7 also shows this ;;
11;; behaviour. ;;
12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
13
14;
15; Usage:
16;
17; To open a filesystem, we need to call FAT_mount.
18;
19; Dealing with directories works as follows:
20; FAT_open: Open a directory or a file in it.
21; FAT_creat: Create a file inside a directory.
22; FAT_unlink: Delete a file from a directory.
23;
24; After that, for dealing with files we can use:
25; FAT_read: Read data from a file.
26; FAT_write: Write data to a file.
27; FAT_trunc: Shorten a file.
28; FAT_seek: Seek forward in a file.
29; FAT_seek_start: Go to start of file.
30; FAT_seek_end: Go to end of file.
31;
32
33; On error of the functions that can error, carry will be set.
34; Also r16 will be loaded with the err no.
35; Additional error numbers are defined in sd.asm.
36.equ ERR_EOF = 0x81 ; Errors under 0x80 are reserved for sd.asm.
37.equ ERR_MNT = 0x82 ; Mounting failed.
38.equ ERR_FNF = 0x83 ; File not found.
39.equ ERR_FSF = 0x84 ; Filesystem full.
40
41; These are offsets starting from FAT_CFG
42; where we need 19 bytes of SRAM storage for globals.
43; All of these are stored in big-endian.
44.equ FAT_START = FAT_CFG + 0 ; dword 4 - Start of filesystem.
45.equ FAT_SPC = FAT_CFG + 4 ; byte 1 - Sectors / cluster.
46.equ FAT_CPY = FAT_CFG + 5 ; byte 1 - FAT copies.
47.equ FAT_SPF = FAT_CFG + 6 ; dword 4 - Sectors per fat.
48.equ FAT_FLAGS = FAT_CFG + 10 ; byte 1 - Mirorring flags.
49.equ FAT_FCL = FAT_CFG + 11 ; dword 4 - First cluster with file data (sectors until).
50.equ FAT_AFAT = FAT_CFG + 15 ; dword 4 - Start of the "active fat".
51.equ FAT_CFS = FAT_CFG + 19 ; dword 4 - Current sector of fat table that we'd search for new clusters.
52; FAT_CFG total size = 23 bytes
53;
54; We could make fat mirroring implied always on and not need FAT_FLAGS.
55; But then what if the first fat is not the primary one?
56
57; These are relative to a "file handle" 22 bytes long.
58; This library also needs FAT_RFN equ'd to a SRAM location for the root file handle.
59.equ FN_DFSE = 0 ; dword: Directory First SEctor.
60.equ FN_DOFF = 4 ; word: Directory entry OFFset (in DFSE, in bytes).
61.equ FN_FCL = 6 ; dword: First CLuster.
62.equ FN_CCL = 10 ; dword: Current CLuster. Bit 31 set if we should make a new cluster before writing.
63.equ FN_POS = 14 ; dword: POSition.
64.equ FN_LEN = 18 ; dword: LENgth.
65.equ FN_SIZE = 22 ; The size of a file handle.
66; Order of these matters for some functions.
67; I try to mark these with the word FNORDER.
68
69;
70; FAT_mount
71;
72; Try to mount FAT32 filesystem on the SD card.
73;
74; Input:
75; -
76;
77; Output:
78; C flag set on error (and r16 loaded with err no).
79;
80; Side effects:
81; r0 (only bit 0), r16, r17, r18, r19, r20, r21, SD_BUF (SRAM), r31:r30 (Z) trashed.
82;
83FAT_mount:
84 ; Clear r17-r20
85 call SD_CMD_clarg
86FAT_mount_try:
87 ; Store what we guessed is the start of filesystem.
88 sts FAT_START + 0, r20
89 sts FAT_START + 1, r19
90 sts FAT_START + 2, r18
91 sts FAT_START + 3, r17
92 call SD_read
93 brcs FAT_mount_end
94 ; Check 55AA signature.
95 lds r16, SD_BUF + 510
96 cpi r16, 0x55
97 brne FAT_mount_fail ; Fail if this is not an MBR.
98 lds r16, SD_BUF + 511
99 cpi r16, 0xAA
100 brne FAT_mount_fail
101 ; Check if this is a FAT partition or just MBR. This seems a bit ugly.
102 lds r16, SD_BUF + 0x52 + 0
103 cpi r16, 'F'
104 brne FAT_mount_findpart
105 lds r16, SD_BUF + 0x52 + 1
106 cpi r16, 'A'
107 brne FAT_mount_findpart
108 lds r16, SD_BUF + 0x52 + 2
109 cpi r16, 'T'
110 brne FAT_mount_findpart
111 lds r16, SD_BUF + 0x52 + 3
112 cpi r16, '3'
113 brne FAT_mount_findpart
114 lds r16, SD_BUF + 0x52 + 4
115 cpi r16, '2'
116 brne FAT_mount_findpart
117 lds r16, SD_BUF + 0x52 + 5
118 cpi r16, ' '
119 brne FAT_mount_findpart
120 lds r16, SD_BUF + 0x52 + 6
121 cpi r16, ' '
122 brne FAT_mount_findpart
123 lds r16, SD_BUF + 0x52 + 7
124 cpi r16, ' '
125 brne FAT_mount_findpart
126 rjmp FAT_mount_ispart ; _findpart and _fail where at the bottom but then it's too far for breq/brne
127 ; Bail out.
128FAT_mount_fail:
129 ldi r16, ERR_MNT
130 sec
131FAT_mount_end:
132 ret
133 ; Attempt to find partition.
134FAT_mount_findpart:
135 ; Check if not we already tried to find a partition.
136 clr r16
137 lds r17, FAT_START + 3
138 or r16, r17
139 lds r17, FAT_START + 2
140 or r16, r17
141 lds r17, FAT_START + 1
142 or r16, r17
143 lds r17, FAT_START + 0
144 or r16, r17
145 brne FAT_mount_fail ; If any bits were set in r17-r19 Z flag is cleared.
146 ; Check if the first partition is fat, and if so try to use it.
147 lds r16, SD_BUF + 0x1BE + 4 ; 0x1BE is first partition in table.
148 cpi r16, 0x0B ; 0x0B or 0x0C partition type is FAT32
149 breq FAT_mount_partok
150 cpi r16, 0x0C
151 breq FAT_mount_partok
152 rjmp FAT_mount_fail
153FAT_mount_partok:
154 ; Load the adress of the partition and retry.
155 lds r17, SD_BUF + 0x1BE + 8 + 0 ; LSB
156 lds r18, SD_BUF + 0x1BE + 8 + 1
157 lds r19, SD_BUF + 0x1BE + 8 + 2
158 lds r20, SD_BUF + 0x1BE + 8 + 3 ; MSB
159 mov r16, r17 ; Check if the LBA is zero.
160 or r16, r18
161 or r16, r19
162 or r16, r20
163 breq FAT_mount_fail ; CHS not supported yet.
164 rjmp FAT_mount_try
165 ; Attempt to mount partition. We only reach here if we have a fat partition.
166FAT_mount_ispart:
167 ; Check if bytes/sector is 512, so we don't destroy stuff on weird FS.
168 lds r16, SD_BUF + 0x0B + 0 ; LSB.
169 cpi r16, 0
170 brne FAT_mount_fail
171 lds r16, SD_BUF + 0x0B + 1 ; MSB.
172 cpi r16, 2
173 brne FAT_mount_fail
174 ; Sectors/cluster. We can optionally check if this is indeed a power of 2.
175 lds r16, SD_BUF + 0x0D
176 sts FAT_SPC, r16
177 ; Reserved sectors count.
178 lds r16, FAT_START + 3
179 lds r17, SD_BUF + 0x0E + 0 ; LSB.
180 add r16, r17
181 sts FAT_START + 3, r16
182 lds r16, FAT_START + 2
183 lds r17, SD_BUF + 0x0E + 1 ; MSB.
184 adc r16, r17
185 sts FAT_START + 2, r16
186 lds r16, FAT_START + 1
187 clr r17 ; Just for carry.
188 adc r16, r17
189 sts FAT_START + 1, r16
190 ; Number of FAT copies.
191 lds r16, SD_BUF + 0x10
192 sts FAT_CPY, r16
193 ; Sectors per fat.
194 lds r16, SD_BUF + 0x24 + 0 ; LSB
195 sts FAT_SPF + 3, r16
196 lds r16, SD_BUF + 0x24 + 1
197 sts FAT_SPF + 2, r16
198 lds r16, SD_BUF + 0x24 + 2
199 sts FAT_SPF + 1, r16
200 lds r16, SD_BUF + 0x24 + 3 ; MSB
201 sts FAT_SPF + 0, r16
202 ; Mirroring flags
203 lds r16, SD_BUF + 0x28
204 sts FAT_FLAGS, r16
205 ; Calculate first cluster that has file data (FAT_SPF * FAT_CPY + FAT_START).
206 clr r20
207 clr r19
208 lds r16, FAT_CPY
209 lds r21, FAT_SPF + 3
210 mul r16, r21
211 mov r18, r1
212 mov r17, r0
213 lds r21, FAT_SPF + 2
214 mul r16, r21
215 add r18, r0
216 adc r19, r1
217 lds r21, FAT_SPF + 1
218 mul r16, r21
219 add r19, r0
220 adc r20, r1
221 lds r21, FAT_SPF + 0
222 mul r16, r21
223 add r20, r0
224 lds r16, FAT_START + 3 ; Add FAT_START while storing in FAT_FCL
225 add r16, r17
226 sts FAT_FCL + 3, r16
227 lds r16, FAT_START + 2
228 adc r16, r18
229 sts FAT_FCL + 2, r16
230 lds r16, FAT_START + 1
231 adc r16, r19
232 sts FAT_FCL + 1, r16
233 lds r16, FAT_START + 0
234 adc r16, r20
235 sts FAT_FCL + 0, r16
236 ; Find the "active" FAT and store its sector number.
237 ; NOTE With fat mirroring it shouldn't really matter which one is used.
238 lds r20, FAT_START + 0
239 lds r19, FAT_START + 1
240 lds r18, FAT_START + 2
241 lds r17, FAT_START + 3
242 lds r16, FAT_FLAGS ; Now multiply this by FAT_SPF and add.
243 andi r16, 0x0F
244 lds r21, FAT_SPF + 3
245 mul r16, r21
246 add r17, r0
247 adc r18, r1
248 lds r21, FAT_SPF + 2
249 mul r16, r21
250 add r18, r0
251 adc r19, r1
252 lds r21, FAT_SPF + 1
253 mul r16, r21
254 add r19, r0
255 adc r20, r1
256 lds r21, FAT_SPF + 0
257 mul r16, r21
258 add r20, r0
259 sts FAT_AFAT + 0, r20
260 sts FAT_AFAT + 1, r19
261 sts FAT_AFAT + 2, r18
262 sts FAT_AFAT + 3, r17
263 ; Clear FAT_CFS.
264 clr r16
265 sts FAT_CFS + 0, r16
266 sts FAT_CFS + 1, r16
267 sts FAT_CFS + 2, r16
268 sts FAT_CFS + 3, r16
269 ; Clear the root file handle.
270 ldi r16, FN_SIZE
271 clr r17
272 ldi r31, high(FAT_RFN)
273 ldi r30, low(FAT_RFN)
274FAT_mount_rootclr:
275 st Z+, r17
276 dec r16
277 brne FAT_mount_rootclr
278 ; Root folder cluster (into FAT_RFN FCL and CCL).
279 lds r16, SD_BUF + 0x2C + 0 ; LSB
280 sts FAT_RFN + FN_FCL + 3, r16
281 sts FAT_RFN + FN_CCL + 3, r16
282 lds r16, SD_BUF + 0x2C + 1
283 sts FAT_RFN + FN_FCL + 2, r16
284 sts FAT_RFN + FN_CCL + 2, r16
285 lds r16, SD_BUF + 0x2C + 2
286 sts FAT_RFN + FN_FCL + 1, r16
287 sts FAT_RFN + FN_CCL + 1, r16
288 lds r16, SD_BUF + 0x2C + 3 ; MSB
289 andi r16, 0xF0 ; Only 28 bits used.
290 sts FAT_RFN + FN_FCL + 0, r16
291 sts FAT_RFN + FN_CCL + 0, r16
292 clc
293 ret
294
295;
296; FAT_open
297;
298; Open a file by file name.
299;
300; Input:
301; X (r27:r26): File handle destination (needs 24 bytes).
302; Y (r29:r28): File name.
303; Z (r31:r30): Directory file handle.
304;
305; Output:
306; X (r27:r26): Original directory file handle.
307; Z (r31:r30): File handle destination copied.
308; C flag set on error (and r16 loaded with errno).
309;
310; Side effects:
311; r0 (only bit 0), r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, SD_BUF (SRAM), r27:r26 (X) trashed, r29:r28 (Y) trashed.
312;
313FAT_open:
314 push r27 ; File handle dst, we need X.
315 push r26
316 call FAT_seek_start
317FAT_open_searchloop:
318 ; Read a sector.
319 call FAT_read_sector
320 brcc +1
321 rjmp FAT_open_fail
322 ; Look for the file in that sector.
323 ldi r27, high(SD_BUF)
324 ldi r26, low(SD_BUF)
325FAT_open_searchloop_file:
326 ld r16, X+
327 cpi r16, 0
328 brne +1
329 rjmp FAT_open_notfound ; End of directory.
330 ldi r24, 11
331FAT_open_searchloop_char:
332 ld r17, Y+
333 cp r16, r17 ; r16 got first byte already at first iteration.
334 breq FAT_open_searchloop_charok
335 ; Fallthrough means char wrong, restore Y.
336 subi r24, 12 ; r24 is 11 for Y+1, 10 for Y+2, etc.
337 add r28, r24 ; For example if Y+2, r24 now is -2.
338 ldi r24, 0xFF
339 adc r29, r24
340 ; Go to next entry.
341 adiw r27:r26, 32
342 andi r26, ~31 ; Align to entry.
343 ; Check if this is the end of sector, if so, seek.
344 cpi r27, high(SD_BUF + 512)
345 brne FAT_open_noseek
346 cpi r26, low(SD_BUF + 512)
347 brne +1
348 rjmp FAT_open_seek
349FAT_open_noseek:
350 rjmp FAT_open_searchloop_file
351FAT_open_searchloop_charok:
352 ld r16, X+ ; 2nd char on first iteration.
353 dec r24
354 brne FAT_open_searchloop_char
355 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
356 ;; Falling through means success. ;;
357 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
358 ; Now we can begin filling the file handle.
359 pop r28 ; File handle target.
360 pop r29 ; We don't need Y (filename) anymore.
361 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
362 ;; We start with FN_DFSE and FN_DOFF ;;
363 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
364 ; Load FN_FCL of dir.
365 adiw r31:r30, FN_FCL ; Z = Directory's first cluster in dir FN.
366 ld r20, Z+ ; r20:..:r17 = FN_FCL
367 ld r19, Z+
368 ld r18, Z+
369 ld r17, Z+
370 subi r17, 2 ; First cluster is second lol.
371 clr r16
372 sbc r18, r16
373 sbc r19, r16
374 sbc r20, r16
375 ; Now we do r24:..:r21 = FN_FCL * FAT_SPC.
376 lds r16, FAT_SPC
377 clr r24
378 clr r23
379 mul r17, r16
380 mov r21, r0
381 mov r22, r1
382 mul r18, r16
383 add r22, r0
384 adc r23, r1
385 mul r19, r16
386 add r23, r0
387 adc r24, r1
388 mul r20, r16
389 add r24, r0
390 ; Add FAT_FCL, start of actual clusters.
391 lds r16, FAT_FCL + 3
392 add r21, r16
393 lds r16, FAT_FCL + 2
394 adc r22, r16
395 lds r16, FAT_FCL + 1
396 adc r23, r16
397 lds r16, FAT_FCL + 0
398 adc r24, r16
399 ; Add FN_POS / 512, to r24:..:r21.
400 adiw r31:r30, FN_POS - (FN_FCL + 4) ; FNORDER get dir at FN_POS.
401 clr r20
402 ld r19, Z+ ; Load r20:..r17 with FN_POS / 512.
403 lsr r19
404 ld r18, Z+
405 ror r18
406 ld r17, Z+
407 ror r17
408 sbiw r31:r30, FN_POS + 3 ; Restore Z.
409 add r21, r17
410 adc r22, r18
411 adc r23, r19
412 adc r24, r20
413 ; Store it to FN_DFSE.
414 st Y+, r24 ; FNORDER Y is start of handle FN_DFSE.
415 st Y+, r23
416 st Y+, r22
417 st Y+, r21
418 ; Subtract SD_BUF from X (in r17:r16), get offset of dir entry in FN_DFSE.
419 mov r16, r26
420 mov r17, r27
421 subi r16, low(SD_BUF) ; Get X - SD_BUF.
422 ldi r18, high(SD_BUF)
423 sbc r17, r18
424 andi r16, ~31 ; Align to start of dir entry.
425 ; Store FN_DOFF.
426 st Y+, r17
427 st Y+, r16 ; FNORDER We're expecting FN_FCL next.
428 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
429 ;; Now the dir entry is stored, store FCL, CCL, LEN, etc. ;;
430 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
431 ; Store the Useful bits of dir entry in r17-r24.
432 adiw r27:r26, 8 ; X is past filename +1. Already offset +1 due to weird loop.
433 ld r19, X+ ; Cluster high word.
434 ld r20, X+ ; Cluster high word.
435 andi r20, 0xF0
436 adiw r27:r26, 4 ; File time is here.
437 ld r17, X+ ; Cluster low word.
438 ld r18, X+ ; Cluster low word.
439 ld r21, X+ ; File size LSB
440 ld r22, X+
441 ld r23, X+
442 ld r24, X+ ; File size MSB
443 sbiw r27:r26, 32 ; FNORDER Set X to start of dir entry.
444 ; Fill file first cluster FN_FCL.
445 ldi r16, 2
446FAT_open_fillcl: ; FNORDER We're expecting FN_FCL in Y.
447 st Y+, r20 ; r20-r17 are first cluster.
448 st Y+, r19
449 st Y+, r18
450 st Y+, r17
451 dec r16 ; FNORDER We depend on FN_FCL and FN_CCL being consecutive.
452 brne FAT_open_fillcl
453 st Y+, r16 ; FNORDER This is where we wanna clear FN_POS.
454 st Y+, r16 ; NOTE Now r16 is 0 because of the loop above, we use that.
455 st Y+, r16
456 st Y+, r16
457 st Y+, r24 ; FNORDER FN_LEN must be here.
458 st Y+, r23 ; r24-r21 are file size.
459 st Y+, r22
460 st Y+, r21
461 sbiw r29:r28, FN_SIZE
462 mov r27, r31 ; Store old Z in X.
463 mov r26, r30
464 mov r31, r29 ; NOTE We move it to Z because other functions use that.
465 mov r30, r28
466 clc
467 ret
468FAT_open_seek:
469 ; Seek forward one sector.
470 ldi r17, 2
471 clr r16
472 call FAT_seek_dir
473 brcs FAT_open_fail
474 ; Check whether this was the last cluster.
475 adiw r31:r30, FN_CCL
476 ld r16, Z
477 sbiw r31:r30, FN_CCL
478 andi r16, 0x80
479 brne FAT_open_notfound
480 ; Continue searching.
481 rjmp FAT_open_searchloop
482FAT_open_notfound:
483 ldi r16, ERR_FNF
484FAT_open_fail:
485 pop r26 ; Target file handle restored.
486 pop r27
487 sec
488 ret
489
490;
491; FAT_read
492;
493; Read bytes from a file and advance the pointer.
494;
495; Input:
496; Y (r29:r28): Data destination.
497; Z (r31:r30): File handle.
498; r23:r22: Length of data to read.
499;
500; Output:
501; Y (SRAM): End of data read (if success). This can tell how much data was read.
502; C flag set on error (and r16 loaded with errno).
503;
504; Side effects:
505; r0 (only bit 0), r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, SD_BUF (SRAM), r27:r26 (X) trashed.
506;
507; Notes:
508; Seeking a distance of 0, will give ERR_EOF.
509;
510; Notes:
511; This doesn't work on directories.
512;
513FAT_read:
514 ; Make sure we're not reading beyond EOF.
515 call FAT_seekmax ; Truncates to max amount we could read.
516 clr r16
517 cp r23, r16
518 cpc r22, r16
519 brne +3 ; If it's 0 this is the end.
520 ldi r16, ERR_EOF
521 sec
522 ret
523 ; Store length for later.
524 push r23
525 push r22
526FAT_read_continue:
527 ; Load a sector.
528 call FAT_read_sector ; This leaves r22, r23 alone, but does trash X.
529 brcs FAT_read_fail
530 ; Find position in sector.
531 ldi r27, high(SD_BUF)
532 ldi r26, low(SD_BUF)
533 adiw r31:r30, FN_POS + 2
534 ld r17, Z+ ; Lower 2 bytes of FN_POS.
535 ld r16, Z+
536 sbiw r31:r30, FN_POS + 4 ; Restore Z.
537 andi r17, 1 ; & 0x1FF gives offset in sector.
538 add r26, r16
539 adc r27, r17
540FAT_read_byte:
541 ; Copy a byte from SD_BUF to DST.
542 ld r16, X+
543 st Y+, r16
544 ; Decrement r23:r22 (len) and loop if nonzero.
545 clr r16
546 subi r22, 1
547 sbc r23, r16
548 cpi r23, 0
549 brne +2
550 cpi r22, 0
551 breq FAT_read_done
552 ; Check if we're past the sector boundary.
553 cpi r27, high(SD_BUF + 512)
554 brne +2
555 cpi r26, low(SD_BUF + 512)
556 breq FAT_read_nextsector
557 rjmp FAT_read_byte
558FAT_read_done:
559 ; Seek to new FN_POS and return.
560 pop r16
561 pop r17
562 call FAT_seek
563 ret ; Error from FAT_seek will just be passed on.
564FAT_read_nextsector:
565 ; Get 512 - FN_POS % 512.
566 adiw r31:r30, FN_POS + 2
567 ld r19, Z+ ; Lower 2 bytes of FN_POS.
568 ld r18, Z+
569 sbiw r31:r30, FN_POS + 4 ; Restore Z.
570 andi r19, 1 ; & 0x1FF gives offset in sector.
571 ldi r17, 2 ; Load 512.
572 clr r16
573 sub r16, r18 ; Subtract that shit.
574 sbc r17, r19
575 ; Subtract it from stored length.
576 pop r18
577 pop r19
578 sub r18, r16
579 sbc r19, r17
580 push r19
581 push r18
582 ; Seek, restore our shit, and resume operation.
583 push r23 ; Save r23:r22, our length.
584 push r22 ; FAT_seek could trash it.
585 call FAT_seek
586 pop r22
587 pop r23
588 brcs FAT_read_fail ; Error check for FAT_seek.
589 rjmp FAT_read_continue
590FAT_read_fail:
591 pop r22
592 pop r23
593 ret
594
595;
596; FAT_write
597;
598; Write bytes to a file and advance the pointer.
599;
600; Input:
601; Y (r29:r28): Data source.
602; Z (r31:r30): File handle.
603; r23:r22: Length of data to write.
604;
605; Output:
606; Y (SRAM): End of data written (if success).
607; C flag set on error (and r16 loaded with errno).
608;
609; Side effects:
610; r0 (only bit 0), r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, SD_BUF (SRAM), r27:r26 (X) trashed.
611;
612FAT_write:
613 ; Make sure there's a cluster.
614 push r22
615 call FAT_firstcluster ; Preserve r23.
616 pop r22
617 brcc +1
618 ret
619 ; Store length for later.
620 push r23
621 push r22
622FAT_write_continue:
623 ; Do make sure we actually have a cluster to load.
624 call FAT_wantcluster ; Preserves r23:r22.
625 brcs FAT_write_fail
626 ; Load a sector.
627 call FAT_read_sector ; This leaves r22, r23 alone, but does trash X.
628 brcs FAT_write_fail ; If FAT_EOF we'll simply create a cluster first.
629 ; Find position in sector.
630 ldi r27, high(SD_BUF)
631 ldi r26, low(SD_BUF)
632 adiw r31:r30, FN_POS + 2
633 ld r17, Z+ ; Lower 2 bytes of FN_POS.
634 ld r16, Z+
635 sbiw r31:r30, FN_POS + 4 ; Restore Z.
636 andi r17, 1 ; & 0x1FF gives offset in sector.
637 add r26, r16
638 adc r27, r17
639FAT_write_byte:
640 ; Copy a byte from SRC to SD_BUF.
641 ld r16, Y+
642 st X+, r16
643 ; Decrement r23:r22 (len) and loop if nonzero.
644 clr r16
645 subi r22, 1
646 sbc r23, r16
647 cpi r23, 0
648 brne +2
649 cpi r22, 0
650 breq FAT_write_done
651 ; Check if we're past the sector boundary.
652 cpi r27, high(SD_BUF + 512)
653 brne +2
654 cpi r26, low(SD_BUF + 512)
655 breq FAT_write_nextsector
656 rjmp FAT_write_byte
657FAT_write_done:
658 ; Write out what we've copied.
659 call FAT_write_sector
660 brcs FAT_write_fail
661 ; Seek to new FN_POS and return.
662 pop r16
663 pop r17
664 call FAT_seek
665 ret ; Error from FAT_seek will just be passed on.
666FAT_write_nextsector:
667 ; Write out the previous sector.
668 call FAT_write_sector
669 brcs FAT_write_fail
670 ; Get 512 - FN_POS % 512.
671 adiw r31:r30, FN_POS + 2
672 ld r19, Z+ ; Lower 2 bytes of FN_POS.
673 ld r18, Z+
674 sbiw r31:r30, FN_POS + 4 ; Restore Z.
675 andi r19, 1 ; & 0x1FF gives offset in sector.
676 ldi r17, 2 ; Load 512.
677 clr r16
678 sub r16, r18 ; Subtract that shit.
679 sbc r17, r19
680 ; Subtract it from stored length.
681 pop r18
682 pop r19
683 sub r18, r16
684 sbc r19, r17
685 push r19
686 push r18
687 ; Seek, restore our shit, and resume operation.
688 push r23 ; Save r23:r22, our length.
689 push r22 ; FAT_seek could trash it.
690 call FAT_seek
691 pop r22
692 pop r23
693 brcs FAT_write_fail ; Error check for FAT_seek.
694 rjmp FAT_write_continue
695FAT_write_fail:
696 pop r22
697 pop r23
698 sec
699 ret
700
701;
702; FAT_creat
703;
704; Create a new file (or partial directory).
705;
706; Input:
707; Y (r29:r28): File name (11 bytes) + attributes (1 byte) + time (2 bytes) + date (2 bytes).
708; Z (r31:r30): Directory file handle.
709;
710; Output:
711; C flag set on error (and r16 loaded with errno).
712;
713; Side effects:
714; r0 (only bit 0), r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, SD_BUF (SRAM), r27:r26 (X) trashed.
715; T flag too.
716;
717; Notes:
718; - This will create duplicate files if one already exists, be careful.
719; - Y is preserved so that one can open the file easily after creation.
720; - In a new file we don't need clusters yet, just set first cluster = 0.
721; - It's a requirement for a folder to have a cluster though.
722;
723FAT_creat:
724 call FAT_seek_start
725 ; Find and load sector of folder.
726FAT_creat_sector:
727 ; Read a sector.
728 call FAT_read_sector
729 brcc +1
730 ret
731 ; Attempt to find dir entry.
732 ldi r27, high(SD_BUF)
733 ldi r26, low(SD_BUF)
734FAT_creat_loop:
735 ld r16, X
736 cpi r16, 0
737 breq FAT_creat_gotent
738 cpi r16, 0xE5
739 breq FAT_creat_gotent
740 adiw r27:r26, 32 ; Size of a directory entry.
741 ; Try again if this ain't the end.
742 cpi r26, low(SD_BUF + 512)
743 brne FAT_creat_loop
744 cpi r27, high(SD_BUF + 512)
745 brne FAT_creat_loop
746 ; No dice, read next sector and retry.
747 ldi r17, 2 ; seek(512)
748 clr r16
749 call FAT_seek_dir
750 brcs FAT_creat_end
751 ; We make sure a sector exists if none is there yet.
752 adiw r31:r30, FN_CCL
753 ld r16, Z
754 sbiw r31:r30, FN_CCL
755 andi r16, 0x80
756 breq FAT_creat_gotcl
757 ; Reaching here means we need a new cluster.
758 call FAT_wantcluster
759 brcs FAT_creat_end
760 ; The cluster should be filled with zeros.
761 call FAT_clearsdbuf
762 ; Now write this to every sector of the cluster.
763 lds r23, FAT_SPC
764 clr r22
765FAT_create_writenewloop:
766 call FAT_find_sector ; Preserve r22, r23.
767 clr r16
768 add r17, r22
769 adc r18, r16
770 adc r19, r16
771 adc r20, r16
772 call SD_write ; Preserve r22, r23.
773 inc r22
774 cp r22, r23
775 brne FAT_create_writenewloop
776FAT_creat_gotcl:
777 rjmp FAT_creat_sector
778FAT_creat_gotent:
779 ; We found the last dir entry, overwrite it with the new file.
780 ldi r16, 12 ; Filename + attr.
781 ld r17, Y+
782 st X+, r17
783 dec r16
784 brne -4
785 clr r18
786 ldi r16, 10 ; Reserved bytes + high byte of FCL.
787 st X+, r18
788 dec r16
789 brne -3
790 ldi r16, 4 ; Time + date.
791 ld r17, Y+
792 st X+, r17
793 dec r16
794 brne -4
795 ldi r16, 6 ; Low word of cluster + file size.
796 st X+, r18 ; r18 was already 0
797 dec r16
798 brne -3
799 ; Restore Y.
800 sbiw r29:r28, 16
801 ; Write sector.
802 call FAT_write_sector
803FAT_creat_end:
804 ret ; Pass error onwards.
805
806;
807; FAT_mkdir
808;
809; Create a new directory and open it.
810;
811; Input:
812; X (r27:r26): Space allocated for new directory.
813; Y (r29:r28): File name (11 bytes) + attributes (1 byte) + time (2 bytes) + date (2 bytes).
814; Z (r31:r30): Directory file handle (parent).
815;
816; Output:
817; Z (r31:r30): Directory file handle (created).
818; C flag set on error (and r16 loaded with errno).
819;
820; Side effects:
821; r0 (only bit 0), r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, SD_BUF (SRAM), r27:r26 (X) trashed.
822; T flag too.
823;
824; Notes:
825; - This will create duplicate files/directories if one already exists, be careful.
826; - User must make sure bit 4 (or with 0x10) of attributes is set (directory flag).
827;
828FAT_mkdir:
829 push r27
830 push r26
831 call FAT_creat
832 pop r26
833 pop r27
834 brcc +1
835 rjmp FAT_mkdir_end
836 call FAT_open ; This will move Z to X.
837 brcc +1
838 rjmp FAT_mkdir_end
839 mov r29, r27 ; Move Z to Y. We won't touch it for a while.
840 mov r28, r26
841 call FAT_firstcluster
842 brcc +1
843 rjmp FAT_mkdir_end
844 ; Fill . and .. nodes, zero pad the whole cluster.
845 call FAT_clearsdbuf
846 ldi r27, high(SD_BUF)
847 ldi r26, low(SD_BUF)
848 ; Write "." dir entry.
849 ldi r16, 0x2E ; File name
850 st X+, r16
851 ldi r16, 0x20
852 ldi r17, 10
853 st X+, r16
854 dec r17
855 brne -3
856 ldi r16, 0x10
857 st X+, r16
858 adiw r27:r26, 8 ; "Reserved"
859 adiw r31:r30, FN_FCL + 2
860 ld r16, -Z ; High word of cluster.
861 st X+, r16
862 ld r16, -Z
863 st X+, r16
864 adiw r27:r26, 4 ; Date and time. NOTE Should we copy from folder?
865 adiw r31:r30, 4
866 ld r16, -Z ; Low word of cluster.
867 st X+, r16
868 ld r16, -Z
869 st X+, r16
870 sbiw r31:r30, FN_FCL + 2 ; Restore Z.
871 adiw r27:r26, 4 ; Size is 0.
872 ; Write ".." dir entry.
873 ldi r16, 0x2E ; File name
874 st X+, r16
875 st X+, r16
876 ldi r16, 0x20
877 ldi r17, 9
878 st X+, r16
879 dec r17
880 brne -3
881 ldi r16, 0x10
882 st X+, r16
883 ; If Y == FAT_RFN we actually want the cluster to be 0.
884 cpi r29, high(FAT_RFN)
885 brne FAT_mkdir_notroot
886 cpi r28, low(FAT_RFN)
887 brne FAT_mkdir_notroot
888 ; NOTE X at reserved part of "..".
889 rjmp FAT_mkdir_wasroot
890FAT_mkdir_notroot:
891 adiw r27:r26, 8 ; "Reserved"
892 adiw r29:r28, FN_FCL + 2
893 ld r16, -Y ; High word of cluster.
894 st X+, r16
895 ld r16, -Y
896 st X+, r16
897 adiw r27:r26, 4 ; Date and time.
898 adiw r29:r28, 4
899 ld r16, -Y ; Low word of cluster.
900 st X+, r16
901 ld r16, -Y
902 st X+, r16
903 sbiw r29:r28, FN_FCL + 2 ; Restore Y.
904 ; NOTE X at file size of "..", different of as it was root folder.
905FAT_mkdir_wasroot:
906 ; Now write this to every sector of the cluster.
907 lds r23, FAT_SPC
908 clr r22
909FAT_mkdir_writenewloop:
910 call FAT_find_sector ; Preserve r22, r23.
911 clr r16
912 add r17, r22
913 adc r18, r16
914 adc r19, r16
915 adc r20, r16
916 call SD_write ; Preserve r22, r23.
917 brcs FAT_mkdir_end
918 cpi r22, 0 ; Clear SD_BUF again after writing first sector.
919 brne +1 ; NOTE this is not very optimal, we could do faster.
920 call FAT_clearsdbuf
921 inc r22
922 cp r22, r23
923 brne FAT_mkdir_writenewloop
924 ; The end.
925 clc
926FAT_mkdir_end:
927 ret
928
929;
930; FAT_unlink
931;
932; Delete a file.
933;
934; Input:
935; Z (r31:r30): File node, file needs to be open to be deleted.
936;
937; Output:
938; C flag set on error (and r16 loaded with errno).
939;
940; Side effects:
941; r0 (only bit 0), r2 (only bit 0), r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, SD_BUF (SRAM), r27:r26 (X) trashed.
942; T flag too.
943;
944; Notes:
945; Do not use this on directories.
946;
947FAT_unlink:
948 ; Get the first cluster.
949 adiw r31:r30, FN_FCL
950 ld r19, Z+ ; See FAT_killcluster and FAT_getclsector for why this is awkward.
951 ld r18, Z+
952 ld r17, Z+
953 ld r20, Z+
954 sbiw r31:r30, FN_FCL + 4 ; Restore Z.
955 clt ; Clear T, as we don't want to write EOF.
956 ; Check if we actually need to do anything.
957 clr r21
958 cp r20, r21
959 cpc r17, r21
960 cpc r18, r21
961 cpc r19, r21 ; No clusters means already at length 0.
962 brne +1
963 rjmp FAT_unlink_continue ; NOTE not error.
964 ; Murder the clusters.
965FAT_unlink_loop:
966 call FAT_killcluster
967 brcc +1
968 rjmp FAT_unlink_fail
969 ; Check if the next one is the last one.
970 ldi r16, 0xFF
971 cp r20, r16
972 cpc r17, r16
973 cpc r18, r16
974 ldi r16, 0x0F
975 cpc r19, r16
976 breq FAT_unlink_end ; NOTE this could loop endlessly on broken filesystem.
977 rjmp FAT_unlink_loop
978FAT_unlink_end:
979 ; Kill the last cluster.
980 call FAT_killcluster
981 brcs FAT_unlink_fail
982FAT_unlink_continue:
983 ; Write dir entry.
984 call FAT_getdent
985 brcs FAT_unlink_fail
986 ldi r16, 0xE5 ; This means "deleted file".
987 st X+, r16
988 ld r20, Z+ ; FNORDER Want FN_DFSE.
989 ld r19, Z+
990 ld r18, Z+
991 ld r17, Z+
992 sbiw r31:r30, FN_DFSE + 4
993 call SD_write
994FAT_unlink_fail:
995 ret ; SD_write error fall through.
996
997;
998; FAT_trunc
999;
1000; Shrink the size of a file to current position.
1001;
1002; Input:
1003; Z (r31:r30): File node.
1004;
1005; Output:
1006; C flag set on error (and r16 loaded with errno).
1007;
1008; Side effects:
1009; r0 (only bit 0), r2 (only bit 0), r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, SD_BUF (SRAM), r27:r26 (X) trashed.
1010; T flag too.
1011;
1012; Notes:
1013; Do not use this on directories, as it will write file size.
1014;
1015FAT_trunc:
1016 ; Clear T if the file should be made empty.
1017 set ; This tells FAT_killcluster to write last sector mark.
1018 adiw r31:r30, FN_POS
1019 clr r18 ; NOTE This whole thing is quite suboptimal.
1020 ld r17, Z+ ; We look at FN_POS later too, this could improve.
1021 or r18, r17
1022 ld r17, Z+
1023 or r18, r17
1024 ld r17, Z+
1025 or r18, r17
1026 ld r17, Z+
1027 or r18, r17
1028 brne +1
1029 clt
1030 bld r2, 0 ; Store T for later.
1031 ; Read the current cluster.
1032 sbiw r31:r30, FN_POS + 4 - FN_CCL ; FNORDER Want FN_CCL.
1033 ld r19, Z+ ; See FAT_killcluster and FAT_getclsector for why this is awkward.
1034 ld r18, Z+
1035 ld r17, Z+
1036 ld r20, Z+
1037 andi r19, 0xF0
1038 sbiw r31:r30, FN_CCL + 4 ; Restore Z.
1039 ; Check if we actually need to do anything.
1040 clr r21
1041 cp r20, r21
1042 cpc r17, r21
1043 cpc r18, r21
1044 cpc r19, r21 ; No clusters means already at length 0.
1045 clc
1046 brne +1
1047 rjmp FAT_trunc_fail ; NOTE not error.
1048 ; Murder the clusters.
1049FAT_trunc_loop:
1050 call FAT_killcluster
1051 brcc +1
1052 rjmp FAT_trunc_fail
1053 ; Check if the next one is the last one.
1054 ldi r16, 0xFF
1055 cp r20, r16
1056 cpc r17, r16
1057 cpc r18, r16
1058 ldi r16, 0x0F
1059 cpc r19, r16
1060 clt ; This tells FAT_killcluster to free a sector.
1061 breq FAT_trunc_end ; NOTE this could loop endlessly on broken filesystem.
1062 rjmp FAT_trunc_loop
1063FAT_trunc_end:
1064 ; Kill the last cluster.
1065 call FAT_killcluster
1066 brcs FAT_trunc_fail
1067 ; Copy FN_POS to FN_LEN.
1068 adiw r31:r30, FN_POS
1069 ld r20, Z+
1070 ld r19, Z+
1071 ld r18, Z+
1072 ld r17, Z+
1073 st Z+, r20 ; FNORDER FN_LEN sequential with FN_POS.
1074 st Z+, r19
1075 st Z+, r18
1076 st Z+, r17
1077 sbiw r31:r30, FN_POS + 8 ; FNORDER
1078 ; If the file is empty, clear FCL and CCL.
1079 bst r2, 0
1080 brts FAT_trunc_nonempty
1081 ; Reaching here means the file has 0-length and we should clear the first sector.
1082 adiw r31:r30, FN_FCL
1083 clr r16
1084 st Z+, r16
1085 st Z+, r16
1086 st Z+, r16
1087 st Z+, r16
1088 st Z+, r16 ; FNORDER FN_CCL after FN_FCL.
1089 st Z+, r16
1090 st Z+, r16
1091 st Z+, r16
1092 sbiw r31:r30, FN_FCL + 8 ; FNORDER
1093FAT_trunc_nonempty:
1094 ; Write dir entry (unless this is a folder).
1095 call FAT_getdent
1096 brcs FAT_trunc_fail
1097 ; Write out FN_FCL, it may or may not be changed.
1098 adiw r31:r30, FN_FCL
1099 ld r20, Z+
1100 andi r20, 0xF0
1101 ld r19, Z+
1102 ld r18, Z+
1103 ld r17, Z+
1104 adiw r27:r26, 0x14 ; FCL high word.
1105 st X+, r19
1106 st X+, r20
1107 adiw r27:r26, 4 ; FCL low word, file size follows this.
1108 st X+, r17
1109 st X+, r18
1110 ; Write out FN_LEN.
1111 adiw r31:r30, FN_LEN + 4 - (FN_FCL + 4) ; FNORDER
1112 ld r16, -Z ; Reverse the byte order.
1113 st X+, r16
1114 ld r16, -Z
1115 st X+, r16
1116 ld r16, -Z
1117 st X+, r16
1118 ld r16, -Z
1119 st X+, r16
1120 sbiw r31:r30, FN_LEN
1121 ld r20, Z+ ; FNORDER Want FN_DFSE.
1122 ld r19, Z+
1123 ld r18, Z+
1124 ld r17, Z+
1125 sbiw r31:r30, FN_DFSE + 4
1126 call SD_write
1127FAT_trunc_fail:
1128 ret ; SD_write error fall through.
1129
1130;
1131; FAT_seek, FAT_seek_dir
1132;
1133; Seek forward in file, also grows files if seeked beyond the end.
1134; FAT_seek_dir is for directories, as those want 0 file size forever.
1135;
1136; Input:
1137; Z (r31:r30): File handle.
1138; r17:r16: How far to seek forward.
1139;
1140; Output:
1141; C flag set on error (and r16 loaded with errno).
1142;
1143; Side effects:
1144; r0 (only bit 0), r18, r19, r20, r21, r22, r23, r24, r25, SD_BUF (SRAM), r27:r26 (X) trashed.
1145; T flag trashed.
1146;
1147; Notes:
1148; If we seek to right at a cluster boundary, a new cluster is not created.
1149; That way we can see to the end of a cluster without enlarging the file.
1150;
1151FAT_seek_dir:
1152 set
1153 rjmp FAT_seek_real
1154FAT_seek:
1155 clt
1156FAT_seek_real:
1157 ; If seeking 0, we got nothing to do and it could fuck with our math.
1158 clr r18
1159 cp r16, r18
1160 cpc r17, r18
1161 brne +2
1162 clc ; Not error.
1163 ret
1164 ; Make sure there's a cluster.
1165 push r17 ; This is also why we run away from 0 argument.
1166 push r16
1167 call FAT_firstcluster
1168 pop r16
1169 pop r17
1170 brcc +1
1171 ret
1172 ; Get FN_POS.
1173 adiw r31:r30, FN_POS
1174 ld r22, Z+
1175 ld r21, Z+
1176 ld r20, Z+
1177 ld r19, Z+
1178 ; We'll need the lower bits to tell whether we crossed a cluster boundary.
1179 mov r24, r20
1180 mov r23, r19
1181 ; Add the offset and store back to FN_POS.
1182 add r19, r16
1183 adc r20, r17
1184 clr r18
1185 adc r21, r18
1186 adc r22, r18
1187 ; Store FN_POS.
1188 st -Z, r19 ; FNORDER End of FN_POS.
1189 st -Z, r20
1190 st -Z, r21
1191 st -Z, r22
1192 ; Check if we're past EOF, if so grow file.
1193 adiw r31:r30, FN_LEN + 4 - FN_POS ; FNORDER Go to end of FN_LEN
1194 ld r18, -Z ; Low byte of FN_LEN.
1195 cp r19, r18
1196 ld r18, -Z ; N.
1197 cpc r20, r18
1198 ld r18, -Z
1199 cpc r21, r18
1200 ld r18, -Z
1201 cpc r22, r18
1202 brcs FAT_seek_noteof ; If LEN > POS, carry is set.
1203 rcall FAT_seek_eof ; Must preserve r24:r23 and r17:r16.
1204 brcc +3
1205 sbiw r31:r30, FN_LEN ; Restore Z.
1206 sec
1207 ret
1208FAT_seek_noteof:
1209 sbiw r31:r30, FN_LEN ; Restore Z, we don't need it anymore.
1210 ; Find the amount of clusters to move forward.
1211 ; (( fn_pos % (FAT_SPC * 512) ) + offset - 1) / 512 / FAT_SPC
1212 ; | Bytes past current cluster | ^ Bytes added.
1213 ; We stored for this lower bytes of old pos in r24:r23.
1214 ; And kept the argument in r17:r16.
1215 lds r18, FAT_SPC
1216 dec r18 ; We make a bit mask that boils down to "% FAT_SPC * 512"
1217 sec ; Lowest bit of r24 is about sectors still.
1218 rol r18 ; So we roll in 1 bit.
1219 and r24, r18 ; Note that we depend on the fact FAT_SPC has only 1 bit set.
1220 add r23, r16 ; + offset
1221 adc r24, r17 ; NOTE FAT_seek_eof must preserve r17:r16.
1222 push r24 ; Store r23:r24 for later.
1223 push r23
1224 clr r18 ; -1
1225 subi r23, 1 ; NOTE This is why we can't seek 0.
1226 sbc r24, r18
1227 lds r18, FAT_SPC
1228 lsr r24 ; We shift once, then we have amount of sectors moved forward.
1229 lsr r18
1230 brne -3 ; Then we shift once more for every bit we can shift FAT_SPC.
1231 ; Skip r24 clusters.
1232 inc r24
1233FAT_seek_nextcluster:
1234 dec r24
1235 breq FAT_seek_foundcluster
1236 push r24
1237 call FAT_nextcluster
1238 pop r24
1239 brcc +3
1240 pop r16
1241 pop r16
1242 ret
1243 rjmp FAT_seek_nextcluster
1244FAT_seek_foundcluster:
1245 ; If the CCL is not loaded we set it's 31th bit.
1246 pop r23 ; Restore r24:r23.
1247 pop r24
1248 lds r18, FAT_SPC
1249 dec r18 ; We make a bit mask that boils down to "% FAT_SPC * 512"
1250 sec ; Lowest bit of r24 is about sectors still.
1251 rol r18 ; So we roll in 1 bit.
1252 and r24, r18 ; Note that we depend on the fact FAT_SPC has only 1 bit set.
1253 cpi r24, 0
1254 brne FAT_seek_foundcluster_nonaligned
1255 cpi r23, 0
1256 brne FAT_seek_foundcluster_nonaligned
1257 adiw r31:r30, FN_CCL
1258 ld r16, Z+
1259 ori r16, 1 << 7
1260 st -Z, r16
1261 sbiw r31:r30, FN_CCL
1262FAT_seek_foundcluster_nonaligned:
1263 ; Returne withe da successe.
1264 clc
1265 ret
1266FAT_seek_eof: ; FNORDER When this gets called FN_LEN is at Z and (new) FN_POS is in r22:r21:r20:r19.
1267 push r24 ; We must preserve r24:r23 and r17:r16. r23 isn't touched by anthing here.
1268 push r17
1269 push r16
1270 ; We copy FN_POS to FN_LEN since we're beyond EOF.
1271 st Z+, r22
1272 st Z+, r21
1273 st Z+, r20
1274 st Z+, r19
1275 sbiw r31:r30, FN_LEN + 4 ; Go back to start of file handle. FNORDER we want FN_DFSE.
1276 ; Check if this is the FAT_seek_dir.
1277 clc ; This ain't an error.
1278 brts FAT_seek_eof_fail
1279 ; Store new file length to file handle.
1280 ld r20, Z+
1281 ld r19, Z+
1282 ld r18, Z+
1283 ld r17, Z+
1284 call SD_read
1285 brcc +2
1286 adiw r31:r30, FN_LEN - 4 ; FNORDER
1287 rjmp FAT_seek_eof_fail
1288 ldi r27, high(SD_BUF + 0x1C) ; File size is at 0x1C in dir entry.
1289 ldi r26, low(SD_BUF + 0x1C)
1290 ld r17, Z+ ; FNORDER We expect FN_DOFF.
1291 ld r16, Z+
1292 add r26, r16
1293 adc r27, r17
1294 adiw r31:r30, FN_LEN + 4 - (FN_DOFF + 2) ; FNORDER Now we want end of FN_LEN.
1295 ld r16, -Z ; We copy and change endianness.
1296 st X+, r16
1297 ld r16, -Z
1298 st X+, r16
1299 ld r16, -Z
1300 st X+, r16
1301 ld r16, -Z
1302 st X+, r16
1303 sbiw r31:r30, FN_LEN ; FNORDER We want FN_DFSE again.
1304 ld r20, Z+
1305 ld r19, Z+
1306 ld r18, Z+
1307 ld r17, Z+
1308 call SD_write ; Don't touch carry after this until ret.
1309 brcc +3
1310 adiw r31:r30, FN_LEN - 4 ; FNORDER Z must be at FN_LEN when we return.
1311 sec ; adiw trashes carry.
1312 ret
1313 adiw r31:r30, FN_LEN - 4 ; FNORDER Z must be at FN_LEN when we return.
1314 clc
1315FAT_seek_eof_fail:
1316 pop r16
1317 pop r17
1318 pop r24
1319 ret ; FAT_nextcluster will take care of adding enough clusters.
1320
1321;
1322; FAT_seek_start
1323;
1324; Seeks to start of file.
1325;
1326; Input:
1327; Z (r31:r30): File handle.
1328;
1329; Output:
1330; -
1331;
1332; Side effects:
1333; r16, r17, r18, r19 trashed.
1334;
1335FAT_seek_start:
1336 ; Get first cluster and copy to current cluster.
1337 adiw r31:r30, FN_FCL
1338 ld r19, Z+
1339 ld r18, Z+
1340 ld r17, Z+
1341 ld r16, Z+
1342 st Z+, r19 ; FNORDER
1343 st Z+, r18
1344 st Z+, r17
1345 st Z+, r16
1346 ; Set FN_POS to 0.
1347 clr r16
1348 st Z+, r16 ; FNORDER
1349 st Z+, r16
1350 st Z+, r16
1351 st Z+, r16
1352 sbiw r31:r30, 12 + FN_FCL ; FNORDER
1353 ret
1354
1355;
1356; FAT_seek_end
1357;
1358; Seeks to end of file.
1359;
1360; Input:
1361; Z (r31:r30): File handle.
1362;
1363; Output:
1364; C flag set on error (and r16 loaded with errno).
1365;
1366; Side effects:
1367; r0 (only bit 0), r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, SD_BUF (SRAM), r27:r26 (X) trashed.
1368;
1369; Notes:
1370; This doesn't work on directories.
1371;
1372FAT_seek_end:
1373 ; Find how far we can seek.
1374 ldi r23, 0xFF
1375 ldi r22, 0xFF
1376 call FAT_seekmax
1377 ; If that's 0 we quit.
1378 clr r16
1379 cp r16, r16
1380 cpc r17, r16
1381 brne +1
1382 ret
1383 ; Seek that far and restart.
1384 mov r16, r22
1385 mov r17, r23
1386 call FAT_seek
1387 rjmp FAT_seek_end
1388
1389;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1390;; Below functions are limited use, for optimizations and low level trickery. ;;
1391;; Also they're important for the rest of the library ;;
1392;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1393
1394;
1395; FAT_read_sector
1396;
1397; Reads current sector of a file. ERR_EOF returned on 0 length file.
1398;
1399; Input:
1400; Z (r31:r30): File handle.
1401; Sector # is rounded down if FN_POS is misaligned.
1402;
1403; Output:
1404; SD_BUF (SRAM): The sector's contents.
1405; C flag set on error (and r16 loaded with errno).
1406;
1407; Side effects:
1408; r0 (only bit 0), r16, r17, r18, r19, r20, r21, r24, r25, r27:r26 (X) trashed.
1409;
1410; Notes:
1411; This would be faster than FAT_read.
1412;
1413FAT_read_sector:
1414 call FAT_find_sector
1415 brcs +1
1416 call SD_read
1417 ret ; SD_read error passes through.
1418
1419;
1420; FAT_write_sector
1421;
1422; Writes current sector of a file.
1423;
1424; Input:
1425; Z (r31:r30): File handle.
1426; Sector # is rounded down if FN_POS is misaligned.
1427; SD_BUF (SRAM): What to write to the sector.
1428;
1429; Output:
1430; C flag set on error (and r16 loaded with errno).
1431;
1432; Side effects:
1433; r0 (only bit 0), r16, r17, r18, r19, r20, r21, r24, r25, r27:r26 (X) trashed.
1434;
1435; Notes:
1436; This would be faster than FAT_write.
1437; For 0-length files this throws ERR_EOF, but that could be easily changed.
1438; See FAT_firstcluster. Files with 0 length have first cluster = 0.
1439;
1440FAT_write_sector:
1441.if 0 ; Uncomment for create cluster on empty file.
1442 call FAT_firstcluster ; NOTE Does trash r22, check code and update description before uncommenting.
1443 brcc +1
1444 ret
1445.endif
1446 call FAT_find_sector
1447 call SD_write
1448 ret
1449
1450;
1451; FAT_seekmax
1452;
1453; Find out whether FN_POS + r23:r22 is more than FN_LEN.
1454; If so r23:r22 is loaded with FN_LEN - FN_POS.
1455; I.E. Find the max distance we can seek forward without crossing EOF.
1456;
1457; Input:
1458; Z (r31:r30): File handle.
1459; r23:r22: The number to work with.
1460;
1461; Output:
1462; r23:r22: Distance to EOF or original value, whichever is smallest.
1463;
1464; Side effects:
1465; r0 (only bit 0), r16, r17, r18, r19, r20, r21 trashed.
1466;
1467; Notes:
1468; This doesn't work on directories.
1469;
1470FAT_seekmax:
1471 ; Load FN_LEN.
1472 adiw r31:r30, FN_LEN
1473 ld r19, Z+
1474 ld r18, Z+
1475 ld r17, Z+
1476 ld r16, Z+
1477 sbiw r31:r30, 4 ; FNORDER We want end of FN_POS now.
1478 ; Subtract FN_POS.
1479 ld r20, -Z
1480 sub r16, r20 ; Which is FN_LEN - FN_POS.
1481 ld r20, -Z
1482 sbc r17, r20
1483 ld r20, -Z
1484 sbc r18, r20
1485 ld r20, -Z
1486 sbc r19, r20
1487 sbiw r31:r30, FN_POS ; Restore Z.
1488 ; FNORDER Z is now FN_POS, r19:r18:r17:r16 is now FN_LEN - FN_POS
1489 ; Check if r23:r22 is bigger.
1490 clr r20
1491 cp r16, r22
1492 cpc r17, r23
1493 cpc r18, r20
1494 cpc r18, r20
1495 brcc FAT_seekmax_nochange
1496 ; r23:r22 was bigger, shrink it.
1497 mov r22, r16
1498 mov r23, r17
1499 ret
1500FAT_seekmax_nochange:
1501 ret
1502
1503;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1504;; Functions below this are only intended for use by the driver itself. ;;
1505;; They probably won't be very useful for anything else. ;;
1506;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1507
1508;
1509; FAT_find_sector
1510;
1511; Find current sector of a file.
1512;
1513; Input:
1514; Z (r31:r30): File handle.
1515;
1516; Output:
1517; r20:r19:r18:r17: The sector index, ready for SD_read/write.
1518; C flag set on error, r16 loaded with ERR_EOF, this for empty file only.
1519;
1520; Side effects:
1521; r16, r21 trashed.
1522;
1523FAT_find_sector:
1524 ; Load the current cluster.
1525 adiw r31:r30, FN_CCL
1526 ld r20, Z+
1527 ld r19, Z+
1528 ld r18, Z+
1529 ld r17, Z+ ; We depend on Z value later.
1530 ; Check if the CCL is one waiting to be created.
1531 andi r20, 0x80
1532 brne FAT_find_sector_eof
1533 ;andi r20, 0xF0 ; NOTE if we'd use other bits we should do this.
1534 ; If there is no cluster, ERR_EOF.
1535 clr r16
1536 cp r17, r16
1537 cpc r18, r16
1538 cpc r19, r16
1539 cpc r20, r16
1540 brne +4
1541FAT_find_sector_eof:
1542 sbiw r31:r30, FN_CCL + 4
1543 ldi r16, ERR_EOF
1544 sec
1545 ret
1546 ; Subtract 2.
1547 subi r17, 2 ; First cluster is 2.
1548 sbc r18, r16
1549 sbc r19, r16
1550 sbc r20, r16
1551 ; Multiply by the sectors/cluster.
1552 lds r16, FAT_SPC
1553FAT_find_sector_shift:
1554 lsr r16
1555 breq FAT_find_sector_endshift
1556 lsl r17
1557 rol r18
1558 rol r19
1559 rol r20
1560 rjmp FAT_find_sector_shift
1561FAT_find_sector_endshift:
1562 ; Add (FN_POS / 512) % FAT_SPC.
1563 adiw r31:r30, FN_POS - (FN_CCL + 4) + 2 ; FNORDER
1564 ld r21, Z
1565 sbiw r31:r30, FN_POS + 2 ; Restore Z cause we don't need it anymore.
1566 lsr r21 ; r21 is now: (FN_POS >> 9) & 0x7F
1567 lds r16, FAT_SPC ; Note that we depend on FAT_SPC only having 1 bit set.
1568 dec r16 ; So top bit never set now.
1569 and r21, r16 ; r21 % FAT_SPC
1570 clr r16
1571 add r17, r21
1572 adc r18, r16
1573 adc r19, r16
1574 adc r20, r16
1575 ; Add FAT_FCL to this.
1576 lds r16, FAT_FCL + 3 ; LSB
1577 add r17, r16
1578 lds r16, FAT_FCL + 2
1579 adc r18, r16
1580 lds r16, FAT_FCL + 1
1581 adc r19, r16
1582 lds r16, FAT_FCL + 0 ; MSB
1583 adc r20, r16
1584 clc
1585 ret
1586
1587;
1588; FAT_getdent
1589;
1590; Load the appropriate sector of containing directory, and put X at dir entry in SD_BUF.
1591;
1592; Input:
1593; Z (r31:r30): File handle.
1594;
1595; Output:
1596; C flag set on error (and r16 loaded with errno).
1597; r27:r26 (X): Points to dir entry in SD_BUF.
1598; SD_BUF (SRAM): Appropriate sector of dir entry.
1599;
1600; Side effects:
1601; r16, r17, r18, r19, r20, r24, r25, SD_BUF (SRAM) trashed.
1602;
1603FAT_getdent:
1604 ld r20, Z+ ; FNORDER FN_DFSE at start.
1605 ld r19, Z+
1606 ld r18, Z+
1607 ld r17, Z+
1608 call SD_read
1609 brcc +3
1610 sbiw r31:r30, FN_DFSE + 4
1611 sec
1612 ret
1613 ld r17, Z+ ; FNORDER FN_DOFF after DFSE.
1614 ld r16, Z+
1615 ldi r27, high(SD_BUF) ; SD_BUF + FN_DOFF = direntry.
1616 ldi r26, low(SD_BUF)
1617 add r26, r16
1618 adc r27, r17
1619 sbiw r31:r30, FN_DFSE + 6 ; Restore Z.
1620 clc
1621 ret
1622
1623;
1624; FAT_nextcluster
1625;
1626; Advance FN_CCL by dancing through the FAT.
1627;
1628; Input:
1629; Z (r31:r30): File handle.
1630;
1631; Output:
1632; C flag set on error (and r16 loaded with errno).
1633; r20:r19:r18:r17: The new FN_CCL.
1634; FN_CCL of file handle also updated.
1635;
1636; Side effects:
1637; r0 (only bit 0), r16, r21, r22, r23, r24, r25, SD_BUF (SRAM), r27:r26 (X) trashed.
1638;
1639FAT_nextcluster:
1640 ; Pointer to the next cluster is in sector: FAT_AFAT + (FN_CCL * 4 / 512).
1641 ; Which equates to (thiscluster >> 7) + (FAT_FLAGS & 0x0F) * FAT_SPF
1642 adiw r31:r30, FN_CCL ; We pretty much keep Z at FN_CCL the whole function.
1643FAT_nextcluster_retry:
1644 call FAT_getclsector ; Get cluster of CCL pointer.
1645 call FAT_addafat ; Add FAT_AFAT.
1646 call SD_read ; Preserve r22.
1647 brcc +1 ; Too far.
1648 rjmp FAT_nextcluster_fail ; Flags set by SD_read.
1649 ; Now we have the sector that holds the pointer.
1650 call FAT_getclsector_load
1651 ld r17, X+ ; It's little endian that's why this is backwards.
1652 ld r18, X+
1653 ld r19, X+
1654 ld r20, X+
1655 ; Check if it's EOF.
1656 ori r20, 0xF0 ; They oughta call it fat28. We or cause we want EOF = 0xFFFFFFFF.
1657 ldi r16, 0xFF
1658 cp r17, r16
1659 cpc r18, r16
1660 cpc r19, r16
1661 cpc r20, r16
1662 brne FAT_nextcluster_exists
1663 call FAT_getfree ; NOTE After this preserve r21 until FAT_getfree_load.
1664 brcs FAT_nextcluster_fail
1665 ; Read FAT where CCL is.
1666 call FAT_getclsector ; Get cluster of CCL pointer. Preserve r22 too now!
1667 call FAT_addafat ; Add FAT_AFAT. Preserve r11, r22.
1668 call SD_read ; Preserve r21, r22.
1669 brcs FAT_nextcluster_fail
1670 call FAT_getclsector_load ; Preserve r21.
1671 ; X now holds pointer to CCL entry in SD_BUF.
1672 call FAT_getfree_load
1673 ; Now r20:..:r17 holds the new cluster, X points to CCL for the file FAT sector.
1674 st X+, r17 ; Little endian.
1675 st X+, r18
1676 st X+, r19
1677 st X+, r20
1678 ; SD_BUF has been updated to hold a new sector for the FAT.
1679 call FAT_getclsector
1680 call FAT_tablewrite ; With the help of this of course.
1681 brcs FAT_nextcluster_fail
1682 ; NOTE On a broken system this can become an endless loop.
1683 rjmp FAT_nextcluster_retry ; We restart ourselves, it should update the file handle now.
1684FAT_nextcluster_exists:
1685 ; Store the next cluster to FN_CCL, which Z is at the end of.
1686 andi r20, 0x0F ; Make it sane again first.
1687 st Z+, r20
1688 st Z+, r19
1689 st Z+, r18
1690 st Z+, r17
1691 sbiw r31:r30, FN_CCL + 4
1692 clc
1693 ret
1694FAT_nextcluster_fail:
1695 sbiw r31:r30, FN_CCL
1696 sec ; sbiw affects carry.
1697 ret
1698
1699;
1700; FAT_getclsector, FAT_getclsector2
1701;
1702; Get the FAT sector # that holds the pointer for next cluster.
1703; FAT_clgetsector2 is same but doesn't load from Z itself.
1704; To load Z for it is convoluted, read the function.
1705;
1706; Input:
1707; Z (r31:r30): Point to a cluster, i.e. file handle + FN_FCL or FN_CCL.
1708;
1709; Output:
1710; r20:r19:r18:r17: The sector # (without FAT_START or FAT_AFAT).
1711; r22: Low byte of cluster, so you can find it in the sector.
1712;
1713; Side effects:
1714; -
1715;
1716; Notes:
1717; See: FAT_getclsector_load, FAT_addafat
1718;
1719FAT_getclsector:
1720 ; Pointer to the next cluster is in sector: FAT_AFAT + (FN_CCL * 4 / 512).
1721 ; Which equates to (cluster >> 7) + (FAT_FLAGS & 0x0F) * FAT_SPF
1722 ld r19, Z+ ; We load it shifted 8 bytes right in r19:r18:r17.
1723 ld r18, Z+
1724 ld r17, Z+
1725 ld r20, Z+ ; r20 holds the LSB for now.
1726 sbiw r31:r30, 4 ; Restore Z.
1727FAT_getclsector2:
1728 andi r19, 0xF0 ; We use the higher bits for info.
1729 mov r22, r20 ; r22 is preserved by SD_read, we want the LS byte of CCL later.
1730 lsl r20 ; Shift the whole conglomeration left a bit.
1731 rol r17
1732 rol r18
1733 rol r19
1734 clr r20
1735 rol r20 ; Now r20:19:18:17 holds FN_CCL >> 7.
1736 ret
1737
1738;
1739; FAT_getclsector_load
1740;
1741; Load X with SD_BUF + offset for cluster pointer.
1742;
1743; Input:
1744; r22: Low byte of cluster # (FAT_getclsector sets this).
1745;
1746; Output:
1747; r27:r26 (X): Pointer to cluster.
1748;
1749; Side effects:
1750; r22, r23 trashed.
1751;
1752FAT_getclsector_load:
1753 ; Load X with the target.
1754 andi r22, 0x7F ; This is the same as r22 % 128, r22 is LSB of FN_CCL.
1755 clr r23 ; * 4, fat entries are 4 boits.
1756 lsl r22
1757 ;rol r23 ; Unnecessary, note & 0x7F earlier.
1758 lsl r22
1759 rol r23
1760 ldi r27, high(SD_BUF) ; Read the actual pointer.
1761 ldi r26, low(SD_BUF)
1762 add r26, r22
1763 adc r27, r23
1764 ret
1765
1766;
1767; FAT_addafat
1768;
1769; Add FAT_AFAT to X
1770;
1771; Input:
1772; r20:r19:r18:r17: Number to add FAT_AFAT to.
1773;
1774; Output:
1775; r20:r19:r18:r17: Same + FAT_AFAT.
1776;
1777; Side effects:
1778; r16 trashed.
1779;
1780; Notes:
1781; See: FAT_getclsector
1782;
1783FAT_addafat:
1784 lds r16, FAT_AFAT + 3
1785 add r17, r16
1786 lds r16, FAT_AFAT + 2
1787 adc r18, r16
1788 lds r16, FAT_AFAT + 1
1789 adc r19, r16
1790 lds r16, FAT_AFAT + 0
1791 adc r20, r16
1792 ret
1793
1794;
1795; FAT_wantcluster
1796;
1797; Make sure the appropriate cluster is in FN_CCL, the current one might not exist.
1798; If a cluster already exists (FN_FCL != 0) just return.
1799; This is for the situation we just seeked to a cluster boundary.
1800;
1801; Input:
1802; Z (r31:r30): File handle.
1803;
1804; Output:
1805; C flag set on error (and r16 loaded with errno).
1806;
1807; Side effects:
1808; r0 (only bit 0), r16, r21, r24, r25, SD_BUF (SRAM), r27:r26 (X) trashed.
1809;
1810; Notes:
1811; This doesn't work for creating the first cluster.
1812;
1813FAT_wantcluster:
1814 ; Bail out if a cluster exists.
1815 adiw r31:r30, FN_CCL
1816 ld r16, Z
1817 sbiw r31:r30, FN_CCL
1818 andi r16, 0x80
1819 breq FAT_wantcluster_end
1820 ; Ask for a free cluster.
1821 push r23 ; We store these because FAT_write cares.
1822 push r22
1823 call FAT_nextcluster
1824 pop r22
1825 pop r23
1826FAT_wantcluster_end:
1827 ret ; FAT_nextcluster error passthrough.
1828
1829;
1830; FAT_firstcluster
1831;
1832; Create the first cluster of a file (i.e. writing to an empty file).
1833; If a cluster already exists (FN_FCL != 0) just return.
1834;
1835; Input:
1836; Z (r31:r30): File handle.
1837;
1838; Output:
1839; C flag set on error (and r16 loaded with errno).
1840;
1841; Side effects:
1842; r0 (only bit 0), r16, r17, r18, r19, r20, r21, r22, r24, r25, SD_BUF (SRAM), r27:r26 (X) trashed.
1843;
1844FAT_firstcluster:
1845 ; Bail out if a cluster exists.
1846 adiw r31:r30, FN_FCL
1847 ld r20, Z+
1848 ld r19, Z+
1849 ld r18, Z+
1850 ld r17, Z+
1851 sbiw r31:r30, FN_FCL + 4
1852 clr r16
1853 cp r17, r16
1854 cpc r18, r16
1855 cpc r19, r16
1856 cpc r20, r16
1857 clc
1858 brne FAT_firstcluster_fail
1859 ; Ask for a free cluster.
1860 call FAT_getfree ; Preserve Y, Z and r22.
1861 brcs FAT_firstcluster_fail
1862 call FAT_getfree_load ; r20:..:r19 now will hold new sector.
1863 ; Save it to FN_FCL, FAT_CCL.
1864 adiw r31:r30, FN_FCL
1865 ldi r16, 2
1866 st Z+, r20
1867 st Z+, r19
1868 st Z+, r18
1869 st Z+, r17
1870 dec r16
1871 brne -6 ; FNORDER FN_FCL and FN_CCL sequential.
1872 ; Load DFSE without destroying r21.
1873 sbiw r31:r30, (FN_FCL + 8) ; FNORDER Get file node start.
1874 push r21 ; From FAT_getfree.
1875 call FAT_getdent
1876 pop r21
1877 brcs FAT_firstcluster_fail
1878 ; Get the cluster back and write it to X.
1879 call FAT_getfree_load ; Preserve X.
1880 adiw r27:r26, 0x14 ; High word of cluster.
1881 st X+, r19 ; Little endian madness.
1882 st X+, r20
1883 adiw r27:r26, 4 ; Skip over time.
1884 st X+, r17 ; Low word of cluster.
1885 st X+, r18
1886 ; Get DFSE adress back and write it.
1887 ld r20, Z+ ; FNORDER Want FN_DFSE.
1888 ld r19, Z+
1889 ld r18, Z+
1890 ld r17, Z+
1891 sbiw r31:r30, FN_DFSE + 4 ; Restore Z.
1892 call SD_write
1893FAT_firstcluster_fail:
1894 ret ; SD_write error passthrough.
1895
1896;
1897; FAT_getfree
1898;
1899; Allocate a new cluster in the FAT.
1900; Used by FAT_nextcluster and FAT_write only.
1901;
1902; Input:
1903; -
1904;
1905; Output:
1906; C flag set on error (and r16 loaded with errno).
1907; r21: Cluster index in the FAT_CFS sector.
1908; FAT_CFS (SRAM): The sector the cluster is in.
1909; Thus: FAT_CFS * 128 + r21 = The cluster #.
1910; See FAT_getfree_load, it can translate this.
1911;
1912; Side effects:
1913; r0 (only bit 0), r16, r17, r18, r19, r20, r22, r24, r25, SD_BUF (SRAM), r27:r26 (X) trashed.
1914;
1915FAT_getfree:
1916 call FAT_getfree_real
1917 brcc FAT_getfree_ok
1918 cpi r16, ERR_FSF
1919 brne FAT_getfree_ok
1920 call FAT_getfree_real ; First ERR_FSF can be a lie.
1921FAT_getfree_ok:
1922 ret
1923FAT_getfree_real:
1924 ; Get "current fat sector".
1925 lds r17, FAT_CFS + 3
1926 lds r18, FAT_CFS + 2
1927 lds r19, FAT_CFS + 1
1928 lds r20, FAT_CFS + 0
1929 ; Add FAT_AFAT.
1930 call FAT_addafat
1931 ; Read that shit.
1932 call SD_read
1933 brcc +1
1934 ret
1935 ; Search for a free cluster (0).
1936 ldi r27, high(SD_BUF)
1937 ldi r26, low(SD_BUF)
1938 clr r21 ; Loop counter.
1939 clr r17 ; We'll use this to compare against.
1940FAT_getfree_loop0:
1941 ld r18, X+ ; These are small endians.
1942 cp r18, r17
1943 ld r18, X+
1944 cpc r18, r17
1945 ld r18, X+
1946 cpc r18, r17
1947 ld r18, X+
1948 brne FAT_getfree_loop0_continue ; We oughta ignore upper bits but andi affects carry.
1949 andi r18, 0x0F
1950 breq FAT_getfree_yes
1951FAT_getfree_loop0_continue:
1952 inc r21
1953 cpi r21, 128 ; 128 clusters in a fat table sector.
1954 brne FAT_getfree_loop0
1955 ; Increment FAT_CFS, compare with FAT_SPF and retry.
1956 lds r17, FAT_CFS + 3
1957 lds r18, FAT_CFS + 2
1958 lds r19, FAT_CFS + 1
1959 lds r20, FAT_CFS + 0
1960 ldi r16, 1
1961 add r17, r16
1962 clr r16
1963 adc r18, r16
1964 adc r19, r16
1965 adc r20, r16
1966 sts FAT_CFS + 3, r17
1967 sts FAT_CFS + 2, r18
1968 sts FAT_CFS + 1, r19
1969 sts FAT_CFS + 0, r20
1970 lds r16, FAT_SPF + 3
1971 cp r17, r16
1972 lds r16, FAT_SPF + 2
1973 cpc r18, r16
1974 lds r16, FAT_SPF + 1
1975 cpc r19, r16
1976 lds r16, FAT_SPF + 0
1977 cpc r20, r16
1978 breq +1 ; Too far.
1979 rjmp FAT_getfree_real
1980 ; Here means nothing found. Reset FAT_CFS and return error.
1981 clr r16
1982 sts FAT_CFS + 3, r16
1983 sts FAT_CFS + 2, r16
1984 sts FAT_CFS + 1, r16
1985 sts FAT_CFS + 0, r16
1986 ldi r16, ERR_FSF
1987 sec
1988 ret
1989FAT_getfree_yes:
1990 ; This means we got a free cluster at FAT_CFS + r21 * 4 (in the FAT).
1991 ; It actually is cluster # FAT_CFS * 128 + r21
1992 ; Also X points to right after the FAT entry.
1993 ldi r17, 0x0F ; Allocate that sector as EOF.
1994 st -X, r17
1995 ldi r17, 0xFF
1996 st -X, r17
1997 st -X, r17
1998 st -X, r17
1999 ; Store the edited FAT sector.
2000 lds r17, FAT_CFS + 3
2001 lds r18, FAT_CFS + 2
2002 lds r19, FAT_CFS + 1
2003 lds r20, FAT_CFS + 0
2004 call FAT_tablewrite ; This doesn't touch r21.
2005 ret ; Errors from SD_write passed through.
2006
2007;
2008; FAT_getfree_load
2009;
2010; Translate the FAT_CFS + r21 returned by FAT_getfree into a cluster number.
2011;
2012; Input:
2013; r21 and FAT_CFS unchanged from FAT_getfree.
2014;
2015; Output:
2016; r20:r19:r18:r17: The cluster.
2017;
2018; Side effects:
2019; Only flags trashed really.
2020;
2021FAT_getfree_load:
2022 lds r20, FAT_CFS + 3 ; Load FAT_CFS * 128.
2023 lds r17, FAT_CFS + 2 ; For why, see description of FAT_getfree.
2024 lds r18, FAT_CFS + 1
2025 lds r19, FAT_CFS + 0
2026 lsl r20
2027 rol r17
2028 rol r18
2029 rol r19
2030 clr r20
2031 rol r20
2032 add r17, r21 ; Add r21, also see FAT_getfree.
2033 clr r16
2034 adc r18, r16
2035 adc r19, r16
2036 adc r20, r16
2037 ret
2038
2039;
2040; FAT_killcluster
2041;
2042; Free a cluster, and load the next one to memory.
2043;
2044; Input:
2045; r20:r19:r18:r17: The cluster to free, rotated 8 bits right.
2046; This is for FAT_getclsector2.
2047; T flat: Set means write last cluster mark.
2048;
2049; Output:
2050; r20:r19:r18:r17: The next cluster (0x0FFFFFFF on EOF), already stripped of upper 4 bits.
2051; In the same rotated format.
2052; C flag set on error, r16 loaded with errno.
2053;
2054; Side effects:
2055; r0 (only bit 0), r16, r21, r22, r23, r24, r25, r27:r26 (X) trashed.
2056;
2057; Notes:
2058; Highly recommend looking at FAT_getclsector code to understand usage.
2059;
2060FAT_killcluster:
2061 ; Get the sector that holds the next cluster pointer.
2062 call FAT_getclsector2
2063 push r20 ; Store this sector so we can write it back.
2064 push r19
2065 push r18
2066 push r17
2067 call FAT_addafat
2068 call SD_read
2069 brcs FAT_killcluster_fail
2070 call FAT_getclsector_load
2071 ; Get back the sector.
2072 pop r17
2073 pop r18
2074 pop r19
2075 pop r20
2076 ; Store next cluster in r24:..:r21.
2077 ld r21, X+
2078 ld r22, X+
2079 ld r23, X+
2080 ld r24, X+
2081 ; Remove cluster pointer from FAT.
2082 brtc FAT_killcluster_noeof
2083 ldi r16, 0x0F ; Write last cluster marker.
2084 st -X, r16
2085 ldi r16, 0xFF
2086 st -X, r16
2087 st -X, r16
2088 st -X, r16
2089 rjmp FAT_killcluster_eofdone
2090FAT_killcluster_noeof:
2091 clr r16 ; Free cluster.
2092 st -X, r16
2093 st -X, r16
2094 st -X, r16
2095 st -X, r16
2096FAT_killcluster_eofdone:
2097 ; Write to FATs, and load the pointer.
2098 push r24 ; Note that r21 and r23 actually don't get trashed.
2099 push r23 ; NOTE We could optimize this, but be careful of _fail.
2100 push r22
2101 push r21
2102 call FAT_tablewrite
2103 brcs FAT_killcluster_fail
2104 pop r20 ; Put the pointer in the correct registers for FAT_getclsector2.
2105 pop r17
2106 pop r18
2107 pop r19
2108 andi r19, 0x0F
2109 clc
2110 ret
2111FAT_killcluster_fail:
2112 pop r16 ; Restore stack and return.
2113 pop r16
2114 pop r16
2115 pop r16
2116 ret
2117
2118;
2119; FAT_tablewrite
2120;
2121; Write a sector of the FAT. This takes FAT mirroring into account.
2122;
2123; Input:
2124; r20:r19:r18:r17: The sector number (inside FAT).
2125; SD_BUF (SRAM): What to write.
2126;
2127; Output:
2128; C flag set on error (and r16 loaded with errno).
2129;
2130; Side effects:
2131; r0 (only bit 0), r16, r17, r18, r19, r20, r22, r24, r25, r27:r26 (X) trashed.
2132;
2133; Notes:
2134; We always write all FATs, since it's easier.
2135;
2136FAT_tablewrite:
2137 ; Add FAT_START.
2138 lds r16, FAT_START + 3
2139 add r17, r16
2140 lds r16, FAT_START + 2
2141 adc r18, r16
2142 lds r16, FAT_START + 1
2143 adc r19, r16
2144 lds r16, FAT_START + 0
2145 adc r20, r16
2146 ; Loop.
2147 lds r22, FAT_CPY
2148FAT_tablewrite_loop:
2149 ; Write the sector in the current copy.
2150 push r20 ; Store position.
2151 push r19
2152 push r18
2153 push r17
2154 call SD_write
2155 pop r17
2156 pop r18
2157 pop r19
2158 pop r20
2159 brcs FAT_tablewrite_end ; Error check for SD_write.
2160 ; Add FAT_SPF.
2161 lds r16, FAT_SPF + 3
2162 add r17, r16
2163 lds r16, FAT_SPF + 2
2164 adc r18, r16
2165 lds r16, FAT_SPF + 1
2166 adc r19, r16
2167 lds r16, FAT_SPF + 0
2168 adc r20, r16
2169 ; Count down and loop.
2170 dec r22
2171 brne FAT_tablewrite_loop
2172 clc
2173FAT_tablewrite_end:
2174 ret
2175
2176;
2177; FAT_clearsdbuf
2178;
2179; Fill SD_BUF with zeros.
2180;
2181; Input:
2182; -
2183;
2184; Output:
2185; SD_BUF (SRAM): Guaranteed zeros.
2186;
2187; Side effects:
2188; r16, r27:r26 (X) trashed.
2189;
2190FAT_clearsdbuf:
2191 ldi r27, high(SD_BUF)
2192 ldi r26, low(SD_BUF)
2193 clr r16
2194FAT_clearsdbuf_loop: ; Fill a sector with zeros.
2195 st X+, r16
2196 cpi r26, low(SD_BUF + 512)
2197 brne FAT_clearsdbuf_loop
2198 cpi r27, high(SD_BUF + 512)
2199 brne FAT_clearsdbuf_loop
2200 ret