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