1;----------------------------------------------------------------------------- 2; fat32.s 3; Copyright (C) 2020 Frank van den Hoef 4; Copyright (C) 2020 Michael Steil 5;----------------------------------------------------------------------------- 6 7 .include "fat32.inc" 8 .include "lib.inc" 9 .include "sdcard.inc" 10 .include "text_input.inc" 11 12 .import sector_buffer, sector_buffer_end, sector_lba 13 14 .import filename_char_ucs2_to_internal, filename_char_internal_to_ucs2 15 .import filename_cp437_to_internal, filename_char_internal_to_cp437 16 .import match_name, match_type 17 18 ; mkfs.s 19 .export load_mbr_sector, write_sector, clear_buffer, set_errno, unmount 20 21 22FLAG_IN_USE = 1<<0 ; Context in use 23FLAG_DIRTY = 1<<1 ; Buffer is dirty 24FLAG_DIRENT = 1<<2 ; Directory entry needs to be updated on close 25 26.struct context 27flags .byte ; Flag bits 28start_cluster .dword ; Start cluster 29cluster .dword ; Current cluster 30lba .dword ; Sector of current cluster 31cluster_sector .byte ; Sector index within current cluster 32bufptr .word ; Pointer within sector_buffer 33file_size .dword ; Size of current file 34file_offset .dword ; Offset in current file 35dirent_lba .dword ; Sector containing directory entry for this file 36dirent_bufptr .word ; Offset to start of directory entry 37eof .byte ; =$ff: EOF has been reached 38.endstruct 39 40CONTEXT_SIZE = 32 41 42.if CONTEXT_SIZE * FAT32_CONTEXTS > 256 43.error "Too many FAT32_CONTEXTS to fit into 256 bytes!" 44.endif 45 46.if .sizeof(context) > CONTEXT_SIZE 47.error "struct context too big!" 48.endif 49 50.struct fs 51; Static filesystem parameters 52mounted .byte ; Flag to indicate the volume is mounted 53rootdir_cluster .dword ; Cluster of root directory 54sectors_per_cluster .byte ; Sectors per cluster 55cluster_shift .byte ; Log2 of sectors_per_cluster 56lba_partition .dword ; Start sector of FAT32 partition 57fat_size .dword ; Size in sectors of each FAT table 58lba_fat .dword ; Start sector of first FAT table 59lba_data .dword ; Start sector of first data cluster 60cluster_count .dword ; Total number of cluster on volume 61lba_fsinfo .dword ; Sector number of FS info 62; Variables 63free_clusters .dword ; Number of free clusters (from FS info) 64free_cluster .dword ; Cluster to start search for free clusters, also holds result of find_free_cluster 65cwd_cluster .dword ; Cluster of current directory 66.endstruct 67 68FS_SIZE = 64 69 70.if FS_SIZE * FAT32_VOLUMES > 256 71.error "Too many FAT32_VOLUMES to fit into 256 bytes!" 72.endif 73 74.if .sizeof(fs) > FS_SIZE 75.error "struct fs too big!" 76.endif 77 78 .bss 79_fat32_bss_start: 80 81fat32_time_year: .byte 0 82fat32_time_month: .byte 0 83fat32_time_day: .byte 0 84fat32_time_hours: .byte 0 85fat32_time_minutes: .byte 0 86fat32_time_seconds: .byte 0 87 88; Temp 89bytecnt: .word 0 ; Used by fat32_write 90tmp_buf: .res 4 ; Used by save_sector_buffer, fat32_rename 91next_sector_arg: .byte 0 ; Used by next_sector to store argument 92tmp_bufptr: .word 0 ; Used by next_sector 93tmp_sector_lba: .dword 0 ; Used by next_sector 94name_offset: .byte 0 95tmp_dir_cluster: .dword 0 96tmp_attrib: .byte 0 ; temporary: attribute when creating a dir entry 97tmp_dirent_flag: .byte 0 98shortname_buf: .res 11 ; Used for shortname creation 99tmp_timestamp: .byte 0 100tmp_filetype: .byte 0 ; Used to match file type in find_dirent 101 102; Temp - LFN 103lfn_index: .byte 0 ; counter when collecting/decoding LFN entries 104lfn_count: .byte 0 ; number of LFN dir entries when reading/creating 105lfn_checksum: .byte 0 ; created or expected LFN checksum 106lfn_char_count: .byte 0 ; counter when decoding LFN characters 107lfn_name_index: .byte 0 ; counter when decoding LFN characters 108tmp_sfn_case: .byte 0 ; flags when decoding SFN characters 109free_entry_count: .byte 0 ; counter when looking for contig. free dir entries 110marked_entry_lba: .res 4 ; mark/rewind data for directory entries 111marked_entry_cluster:.res 4 112marked_entry_offset: .res 2 113tmp_entry: .res 21 ; SFN entry fields except name, saved during rename 114lfn_buf: .res 20*32 ; create/collect LFN; 20 dirents (13c * 20 > 255c) 115 116; API arguments and return data 117fat32_dirent: .tag dirent ; Buffer containing decoded directory entry 118fat32_size: .res 4 ; Used for fat32_read, fat32_write, fat32_get_offset, fat32_get_free_space 119fat32_errno: .byte 0 ; Last error 120fat32_readonly: .byte 0 ; User-accessible read-only flag 121 122; Contexts 123context_idx: .byte 0 ; Index of current context 124cur_context: .tag context ; Current file descriptor state 125contexts_inuse: .res FAT32_CONTEXTS 126.if ::FAT32_VOLUMES > 1 127volume_for_context: .res FAT32_CONTEXTS 128.endif 129 130; Volumes 131volume_idx: .byte 0 ; Index of current filesystem 132cur_volume: .tag fs ; Current file descriptor state 133 134.if FAT32_CONTEXTS > 1 135contexts: .res CONTEXT_SIZE * FAT32_CONTEXTS 136.endif 137 138.if FAT32_VOLUMES > 1 139volumes: .res FS_SIZE * FAT32_VOLUMES 140.endif 141 142_fat32_bss_end: 143 144 .code 145 146;----------------------------------------------------------------------------- 147; set_volume 148; 149; In: a volume 150; c =1: don't mount 151; 152; * c=0: failure 153;----------------------------------------------------------------------------- 154set_volume: 155 php ; mount flag 156 157 ; Already selected? 158 cmp volume_idx 159 bne @0 160 plp 161 sec 162 rts 163 164@0: 165 ; Valid volume index? 166 cmp #FAT32_VOLUMES 167 bcc @ok 168 169 plp 170 lda #ERRNO_NO_FS 171 jmp set_errno 172 173@ok: 174.if ::FAT32_VOLUMES > 1 175 ; Save new volume index 176 pha 177 178 .assert FS_SIZE = 64, error 179 ; Copy current volume back 180 lda volume_idx 181 bmi @dont_write_back ; < 0 = no current volume 182 asl ; X=A*64 183 asl 184 asl 185 asl 186 asl 187 asl 188 tax 189 190 ldy #0 191@1: lda cur_volume, y 192 sta volumes, x 193 inx 194 iny 195 cpy #(.sizeof(fs)) 196 bne @1 197 198@dont_write_back: 199 ; Copy new volume to current 200 pla ; Get new volume idx 201 pha 202 asl ; X=A*64 203 asl 204 asl 205 asl 206 asl 207 asl 208 tax 209 210 ldy #0 211@2: lda volumes, x 212 sta cur_volume, y 213 inx 214 iny 215 cpy #(.sizeof(fs)) 216 bne @2 217 218 pla 219.endif 220 221 sta volume_idx 222 223 plp 224 bcs @done ; don't mount 225 bit cur_volume + fs::mounted 226 bmi @done 227 lda volume_idx 228 jmp mount 229@done: 230 sec 231 rts 232 233;----------------------------------------------------------------------------- 234; set_errno 235; 236; Only set errno if it wasn't already set. 237; If a read error causes a file not found error, it's still a read error. 238;----------------------------------------------------------------------------- 239set_errno: 240 clc 241 pha 242 lda fat32_errno 243 bne @1 244 pla 245 sta fat32_errno 246 rts 247 248@1: pla 249 rts 250 251;----------------------------------------------------------------------------- 252; sync_sector_buffer 253; 254; * c=0: failure; sets errno 255;----------------------------------------------------------------------------- 256sync_sector_buffer: 257 ; Write back sector buffer if dirty 258 lda cur_context + context::flags 259 bit #FLAG_DIRTY 260 beq @done 261 jmp save_sector_buffer 262 263@done: sec 264 rts 265 266;----------------------------------------------------------------------------- 267; load_sector_buffer 268; 269; * c=0: failure; sets errno 270;----------------------------------------------------------------------------- 271load_sector_buffer: 272 ; Check if sector is already loaded 273 cmp32_ne cur_context + context::lba, sector_lba, @do_load 274 sec 275 rts 276 277@do_load: 278 jsr sync_sector_buffer 279 set32 sector_lba, cur_context + context::lba 280 jsr sdcard_read_sector 281 bcc @1 282 rts 283 284@1: 285 lda #ERRNO_READ 286 jmp set_errno 287 288;----------------------------------------------------------------------------- 289; write_sector 290; 291; * c=0: failure; sets errno 292;----------------------------------------------------------------------------- 293write_sector: 294 lda fat32_readonly 295 bne @error 296 jmp sdcard_write_sector 297 298@error: lda #ERRNO_WRITE_PROTECT_ON 299 jmp set_errno 300 301;----------------------------------------------------------------------------- 302; save_sector_buffer 303; 304; * c=0: failure; sets errno 305;----------------------------------------------------------------------------- 306save_sector_buffer: 307 ; Determine if this is FAT area write (sector_lba - lba_fat < fat_size) 308 sub32 tmp_buf, sector_lba, cur_volume + fs::lba_fat 309 lda tmp_buf + 2 310 ora tmp_buf + 3 311 bne @normal 312 sec 313 lda tmp_buf + 0 314 sbc cur_volume + fs::fat_size + 0 315 lda tmp_buf + 1 316 sbc cur_volume + fs::fat_size + 1 317 bcs @normal 318 319 ; Write second FAT 320 set32 tmp_buf, sector_lba 321 add32 sector_lba, sector_lba, cur_volume + fs::fat_size 322 jsr write_sector 323 php 324 set32 sector_lba, tmp_buf 325 plp 326 bcc @error_write 327 328@normal: 329 jsr write_sector 330 bcc @error_write 331 332 ; Clear dirty bit 333 lda cur_context + context::flags 334 and #(FLAG_DIRTY ^ $FF) 335 sta cur_context + context::flags 336 337 sec 338 rts 339 340@error_write: 341 lda #ERRNO_WRITE 342 jmp set_errno 343 344;----------------------------------------------------------------------------- 345; calc_cluster_lba 346;----------------------------------------------------------------------------- 347calc_cluster_lba: 348 ; lba = lba_data + ((cluster - 2) << cluster_shift) 349 sub32_val cur_context + context::lba, cur_context + context::cluster, 2 350 ldy cur_volume + fs::cluster_shift 351 beq @shift_done 352@1: shl32 cur_context + context::lba 353 dey 354 bne @1 355@shift_done: 356 357 add32 cur_context + context::lba, cur_context + context::lba, cur_volume + fs::lba_data 358 stz cur_context + context::cluster_sector 359 rts 360 361;----------------------------------------------------------------------------- 362; load_fat_sector_for_cluster 363; 364; Load sector that hold cluster entry for cur_context.cluster 365; On return fat32_bufptr points to cluster entry in sector_buffer. 366; 367; * c=0: failure; sets errno 368;----------------------------------------------------------------------------- 369load_fat_sector_for_cluster: 370 ; Calculate sector where cluster entry is located 371 372 ; lba = lba_fat + (cluster / 128) 373 lda cur_context + context::cluster + 1 374 sta cur_context + context::lba + 0 375 lda cur_context + context::cluster + 2 376 sta cur_context + context::lba + 1 377 lda cur_context + context::cluster + 3 378 sta cur_context + context::lba + 2 379 stz cur_context + context::lba + 3 380 lda cur_context + context::cluster + 0 381 asl ; upper bit in C 382 rol cur_context + context::lba + 0 383 rol cur_context + context::lba + 1 384 rol cur_context + context::lba + 2 385 rol cur_context + context::lba + 3 386 add32 cur_context + context::lba, cur_context + context::lba, cur_volume + fs::lba_fat 387 388 ; Read FAT sector 389 jsr load_sector_buffer 390 bcs @1 391 rts ; Failure 392@1: 393 ; fat32_bufptr = sector_buffer + (cluster & 127) * 4 394 lda cur_context + context::cluster 395 asl 396 asl 397 sta fat32_bufptr + 0 398 lda #0 399 bcc @2 400 lda #1 401@2: sta fat32_bufptr + 1 402 add16_val fat32_bufptr, fat32_bufptr, sector_buffer 403 404 ; Success 405 sec 406 rts 407 408;----------------------------------------------------------------------------- 409; is_end_of_cluster_chain 410;----------------------------------------------------------------------------- 411is_end_of_cluster_chain: 412 ; Check if this is the end of cluster chain (entry >= 0x0FFFFFF8) 413 lda cur_context + context::cluster + 3 414 and #$0F ; Ignore upper 4 bits 415 cmp #$0F 416 bne @no 417 lda cur_context + context::cluster + 2 418 cmp #$FF 419 bne @no 420 lda cur_context + context::cluster + 1 421 cmp #$FF 422 bne @no 423 lda cur_context + context::cluster + 0 424 cmp #$F8 425 bcs @yes 426@no: clc 427@yes: rts 428 429;----------------------------------------------------------------------------- 430; next_cluster 431; 432; * c=0: failure; sets errno 433;----------------------------------------------------------------------------- 434next_cluster: 435 ; End of cluster chain? 436 jsr is_end_of_cluster_chain 437 bcs @error 438 439 ; Load correct FAT sector 440 jsr load_fat_sector_for_cluster 441 bcc @error 442 443 ; Copy next cluster from FAT 444 ldy #0 445@1: lda (fat32_bufptr), y 446 sta cur_context + context::cluster, y 447 iny 448 cpy #4 449 bne @1 450 451 sec 452 rts 453 454@error: clc 455 rts 456 457;----------------------------------------------------------------------------- 458; unlink_cluster_chain 459; 460; * c=0: failure; sets errno 461;----------------------------------------------------------------------------- 462unlink_cluster_chain: 463 ; Don't unlink cluster 0 464 lda cur_context + context::cluster + 0 465 ora cur_context + context::cluster + 1 466 ora cur_context + context::cluster + 2 467 ora cur_context + context::cluster + 3 468 bne @next 469 sec 470 rts 471 472@next: jsr next_cluster 473 bcs @0 474 lda fat32_errno 475 beq @done 476 clc 477 rts 478 479@0: 480 ; Set this cluster as new search start point if lower than current start point 481 ldy #3 482 lda cur_volume + fs::free_cluster + 3 483 cmp (fat32_bufptr), y 484 bcc @2 485 dey 486 lda cur_volume + fs::free_cluster + 2 487 cmp (fat32_bufptr), y 488 bcc @2 489 dey 490 lda cur_volume + fs::free_cluster + 1 491 cmp (fat32_bufptr), y 492 bcc @2 493 dey 494 lda cur_volume + fs::free_cluster + 0 495 cmp (fat32_bufptr), y 496 bcc @2 497 beq @2 498 499 ldy #0 500@1: lda (fat32_bufptr), y 501 sta cur_volume + fs::free_cluster, y 502 iny 503 cpy #4 504 bne @1 505@2: 506 ; Set entry as free 507 lda #0 508 ldy #0 509 sta (fat32_bufptr), y 510 iny 511 sta (fat32_bufptr), y 512 iny 513 sta (fat32_bufptr), y 514 iny 515 sta (fat32_bufptr), y 516 517 ; Increment free clusters 518 inc32 cur_volume + fs::free_clusters 519 520 ; Set sector as dirty 521 lda cur_context + context::flags 522 ora #FLAG_DIRTY 523 sta cur_context + context::flags 524 525 bra @next 526 527 ; Make sure dirty sectors are written to disk 528@done: jsr sync_sector_buffer 529 bcs @3 530 rts 531 532@3: jmp update_fs_info 533 534;----------------------------------------------------------------------------- 535; find_free_cluster 536; 537; * c=0: failure; sets errno 538;----------------------------------------------------------------------------- 539find_free_cluster: 540 ; Start search at free_cluster 541 set32 cur_context + context::cluster, cur_volume + fs::free_cluster 542 jsr load_fat_sector_for_cluster 543 bcs @next 544 rts 545 546@next: ; Check for free entry 547 ldy #3 548 lda (fat32_bufptr), y 549 and #$0F ; Ignore upper 4 bits of 32-bit entry 550 dey 551 ora (fat32_bufptr), y 552 dey 553 ora (fat32_bufptr), y 554 dey 555 ora (fat32_bufptr), y 556 bne @not_free 557 558 ; Return found free cluster 559 set32 cur_volume + fs::free_cluster, cur_context + context::cluster 560 sec 561 rts 562 563@not_free: 564 ; fat32_bufptr += 4 565 add16_val fat32_bufptr, fat32_bufptr, 4 566 567 ; cluster += 1 568 inc32 cur_context + context::cluster 569 570 ; Check if at end of FAT table 571 cmp32_ne cur_context + context::cluster, cur_volume + fs::cluster_count, @1 572 clc 573 rts 574@1: 575 ; Load next FAT sector if at end of buffer 576 cmp16_val_ne fat32_bufptr, sector_buffer_end, @next 577 inc32 cur_context + context::lba 578 jsr load_sector_buffer 579 bcs @2 580 rts 581@2: set16_val fat32_bufptr, sector_buffer 582 jmp @next 583 584;----------------------------------------------------------------------------- 585; fat32_alloc_context 586; 587; In: a volume 588; Out: a context 589; c =0: failure 590; errno =ERRNO_OUT_OF_RESOURCES: all contexts in use 591; =ERRNO_READ : error mounting volume 592; =ERRNO_WRITE : error mounting volume 593; =ERRNO_NO_FS : error mounting volume 594; =ERRNO_FS_INCONSISTENT : error mounting volume 595;----------------------------------------------------------------------------- 596fat32_alloc_context: 597 stz fat32_errno 598 599.if FAT32_VOLUMES > 1 600 tay ; volume 601.endif 602 ldx #0 603@1: lda contexts_inuse, x 604 beq @found_free 605 inx 606 cpx #FAT32_CONTEXTS 607 bne @1 608 609 lda #ERRNO_OUT_OF_RESOURCES 610 jmp set_errno 611 612@found_free: 613 lda #1 614 sta contexts_inuse, x 615 616.if FAT32_VOLUMES > 1 617 tya 618 sta volume_for_context, x 619 phx 620 cmp #$ff 621 sec 622 beq @2 623 clc 624 jsr set_volume 625@2: pla 626 bcs @rts 627 jsr fat32_free_context 628 clc 629 rts 630.else 631 txa 632 sec 633.endif 634@rts: 635 rts 636 637;----------------------------------------------------------------------------- 638; fat32_free_context 639; 640; In: a context 641; Out: c =0: failure 642;----------------------------------------------------------------------------- 643fat32_free_context: 644 cmp #FAT32_CONTEXTS 645 bcc @1 646@fail: clc 647 rts 648@1: 649 tax 650 lda contexts_inuse, x 651 beq @fail 652 stz contexts_inuse, x 653 sec 654 rts 655 656;----------------------------------------------------------------------------- 657; fat32_get_num_contexts 658; 659; Out: a number of contexts in use 660;----------------------------------------------------------------------------- 661fat32_get_num_contexts: 662 ldy #0 663 ldx #0 664@1: lda contexts_inuse,x 665 beq @2 666 iny 667@2: inx 668 cpx #FAT32_CONTEXTS 669 bne @1 670 tya 671 rts 672 673;----------------------------------------------------------------------------- 674; update_fs_info 675; 676; * c=0: failure; sets errno 677;----------------------------------------------------------------------------- 678update_fs_info: 679 ; Load FS info sector 680 set32 cur_context + context::lba, cur_volume + fs::lba_fsinfo 681 jsr load_sector_buffer 682 bcs @1 683 rts 684@1: 685 ; Get number of free clusters 686 set32 sector_buffer + 488, cur_volume + fs::free_clusters 687 688 ; Save sector 689 jmp save_sector_buffer 690 691;----------------------------------------------------------------------------- 692; allocate_cluster 693; 694; * c=0: failure; sets errno 695;----------------------------------------------------------------------------- 696allocate_cluster: 697 ; Find free entry 698 jsr find_free_cluster 699 bcs @1 700 rts 701@1: 702 ; Set cluster as end-of-chain 703 ldy #0 704 lda #$FF 705 sta (fat32_bufptr), y 706 iny 707 sta (fat32_bufptr), y 708 iny 709 sta (fat32_bufptr), y 710 iny 711 lda (fat32_bufptr), y 712 ora #$0F ; Preserve upper 4 bits 713 sta (fat32_bufptr), y 714 715 ; Save FAT sector 716 jsr save_sector_buffer 717 bcs @2 718 rts 719@2: 720 ; Decrement free clusters and update FS info 721 dec32 cur_volume + fs::free_clusters 722 jmp update_fs_info 723 724;----------------------------------------------------------------------------- 725; validate_char 726;----------------------------------------------------------------------------- 727validate_char: 728 cmp #$22 ; quote 729 beq @not_ok 730 cmp #'*' 731 beq @not_ok 732 cmp #'/' 733 beq @not_ok 734 cmp #':' 735 beq @not_ok 736 cmp #'<' 737 beq @not_ok 738 cmp #'>' 739 beq @not_ok 740 cmp #'?' 741 beq @not_ok 742 cmp #'\' 743 beq @not_ok 744 cmp #'|' 745 beq @not_ok 746 747 sec 748 rts 749 750@not_ok: 751 clc 752 rts 753 754;----------------------------------------------------------------------------- 755; create_shortname 756; 757; * c=0: failure; sets errno 758;----------------------------------------------------------------------------- 759create_shortname: 760 ldx #0 761 lda marked_entry_lba + 3 762 jsr hexbuf8 763 lda marked_entry_lba + 2 764 jsr hexbuf8 765 lda marked_entry_lba + 1 766 jsr hexbuf8 767 lda marked_entry_lba + 0 768 jsr hexbuf8 769 lda #'~' 770 sta shortname_buf, x 771 inx 772 lda fat32_bufptr + 0 773 sec 774 sbc #<sector_buffer 775 pha 776 lda fat32_bufptr + 1 777 sbc #>sector_buffer 778 lsr 779 pla 780 ror 781 lsr 782 lsr 783 lsr 784 lsr 785 jsr hexbuf4 786 lda #'~' 787 sta shortname_buf, x 788 789 ; Checksum 790 lda #0 791 tay 792@checksum_loop: 793 tax 794 lsr 795 txa 796 ror 797 clc 798 adc shortname_buf, y 799 iny 800 cpy #11 801 bne @checksum_loop 802 sta lfn_checksum 803 rts 804 805hexbuf8: 806 pha 807 lsr 808 lsr 809 lsr 810 lsr 811 jsr hexbuf4 812 pla 813hexbuf4: 814 and #$0f 815 cmp #$0a 816 bcc :+ 817 adc #$66 818: eor #$30 819 sta shortname_buf, x 820 inx 821 rts 822 823;----------------------------------------------------------------------------- 824; open_cluster 825; 826; * c=0: failure; sets errno 827;----------------------------------------------------------------------------- 828open_cluster: 829 ; Check if cluster == 0 -> modify into root dir 830 lda cur_context + context::cluster + 0 831 ora cur_context + context::cluster + 1 832 ora cur_context + context::cluster + 2 833 ora cur_context + context::cluster + 3 834 bne readsector 835 836open_rootdir: 837 set32 cur_context + context::cluster, cur_volume + fs::rootdir_cluster 838 839readsector: 840 ; Read first sector of cluster 841 jsr calc_cluster_lba 842 jsr load_sector_buffer 843 bcc @done 844 845 ; Reset buffer pointer 846 set16_val fat32_bufptr, sector_buffer 847 848 sec 849@done: rts 850 851;----------------------------------------------------------------------------- 852; clear_buffer 853;----------------------------------------------------------------------------- 854clear_buffer: 855 ldy #0 856 tya 857@1: sta sector_buffer, y 858 sta sector_buffer + 256, y 859 iny 860 bne @1 861 rts 862 863;----------------------------------------------------------------------------- 864; clear_cluster 865; 866; * c=0: failure; sets errno 867;----------------------------------------------------------------------------- 868clear_cluster: 869 ; Fill sector buffer with 0 870 jsr clear_buffer 871 872 ; Write sectors 873 jsr calc_cluster_lba 874@2: set32 sector_lba, cur_context + context::lba 875 jsr write_sector 876 bcs @3 877 rts 878@3: lda cur_context + context::cluster_sector 879 inc 880 cmp cur_volume + fs::sectors_per_cluster 881 beq @wrdone 882 sta cur_context + context::cluster_sector 883 inc32 cur_context + context::lba 884 bra @2 885 886@wrdone: 887 sec 888 rts 889 890;----------------------------------------------------------------------------- 891; next_sector 892; A: bit0 - allocate cluster if at end of cluster chain 893; bit1 - clear allocated cluster 894; 895; * c=0: failure; sets errno 896;----------------------------------------------------------------------------- 897next_sector: 898 ; Save argument 899 sta next_sector_arg 900 901 ; Last sector of cluster? 902 lda cur_context + context::cluster_sector 903 inc 904 cmp cur_volume + fs::sectors_per_cluster 905 beq @end_of_cluster 906 sta cur_context + context::cluster_sector 907 908 ; Load next sector 909 inc32 cur_context + context::lba 910@read_sector: 911 jsr load_sector_buffer 912 bcc @error 913 set16_val fat32_bufptr, sector_buffer 914 sec 915 rts 916 917@end_of_cluster: 918 jsr next_cluster 919 bcc @error 920 jsr is_end_of_cluster_chain 921 bcs @end_of_chain 922@read_cluster: 923 jsr calc_cluster_lba 924 bra @read_sector 925 926@end_of_chain: 927 ; Request to allocate new cluster? 928 lda next_sector_arg 929 bit #$01 930 beq @error 931 932 ; Save location of cluster entry in FAT 933 set16 tmp_bufptr, fat32_bufptr 934 set32 tmp_sector_lba, sector_lba 935 936 ; Allocate a new cluster 937 jsr allocate_cluster 938 bcc @error 939 940 ; Load back the cluster sector 941 set32 cur_context + context::lba, tmp_sector_lba 942 jsr load_sector_buffer 943 bcs @1 944@error: clc 945 rts 946@1: 947 set16 fat32_bufptr, tmp_bufptr 948 949 ; Write allocated cluster number in FAT 950 ldy #0 951@2: lda cur_volume + fs::free_cluster, y 952 sta (fat32_bufptr), y 953 iny 954 cpy #4 955 bne @2 956 957 ; Save FAT sector 958 jsr save_sector_buffer 959 bcc @error 960 961 ; Set allocated cluster as current 962 set32 cur_context + context::cluster, cur_volume + fs::free_cluster 963 964 ; Request to clear new cluster? 965 lda next_sector_arg 966 bit #$02 967 beq @wrdone 968 jsr clear_cluster 969 bcc @error 970 971@wrdone: 972 ; Retry 973 jmp @read_cluster 974 975;----------------------------------------------------------------------------- 976; find_dirent 977; 978; Find directory entry with path specified in string pointed to by fat32_ptr 979; 980; In: a =$00 allow files and directories 981; =$80 only allow files 982; =$40 only allow directories 983; 984; * c=0: failure; sets errno 985;----------------------------------------------------------------------------- 986find_dirent: 987 sta tmp_filetype 988 stz name_offset 989 990 ; If path starts with a slash, use root directory as base, 991 ; otherwise the current directory. 992 lda (fat32_ptr) 993 cmp #'/' 994 bne @use_current 995 set32_val cur_context + context::cluster, 0 996 inc name_offset 997 998 ; Does path only consists of a slash? 999 ldy name_offset 1000 lda (fat32_ptr), y 1001 bne @open 1002 1003 ; Fake a directory entry for the root directory 1004 lda #'/' 1005 sta fat32_dirent + dirent::name 1006 stz fat32_dirent + dirent::name + 1 1007 lda #$10 1008 sta fat32_dirent + dirent::attributes 1009 .assert dirent::start < dirent::size, error ; must be next to each other 1010 ldx #0 1011@clr: stz fat32_dirent + dirent::start, x 1012 inx 1013 cpx #8 1014 bne @clr 1015 1016 sec 1017 rts 1018 1019@use_current: 1020 set32 cur_context + context::cluster, cur_volume + fs::cwd_cluster 1021 1022@open: set32 tmp_dir_cluster, cur_context + context::cluster 1023 1024 jsr open_cluster 1025 bcc @error 1026 1027@next: ; Read entry 1028 jsr fat32_read_dirent 1029 bcc @error 1030 1031 ldy name_offset 1032 jsr match_name 1033 bcc @next 1034 1035 ; Check for '/' 1036 lda (fat32_ptr), y 1037 cmp #'/' 1038 beq @chdir 1039 1040 lda fat32_dirent + dirent::attributes 1041 bit #$10 1042 bne @is_dir 1043 ; is file 1044 bit tmp_filetype 1045 bvs @next 1046 bra @ok 1047@is_dir: 1048 bit tmp_filetype 1049 bmi @next 1050@ok: jsr match_type 1051 bcc @next 1052 1053@found: ; Found 1054 sec 1055 rts 1056 1057@error: clc 1058 rts 1059 1060@chdir: iny 1061 lda (fat32_ptr), y 1062 beq @found 1063 1064 ; Is this a directory? 1065 lda fat32_dirent + dirent::attributes 1066 bit #$10 1067 beq @error 1068 1069 sty name_offset 1070 1071 set32 cur_context + context::cluster, fat32_dirent + dirent::start 1072 set32 tmp_dir_cluster, fat32_dirent + dirent::start 1073 jmp @open 1074 1075;----------------------------------------------------------------------------- 1076; find_file 1077; 1078; Same as find_dirent, but with file type check 1079; 1080; * c=0: failure; sets errno 1081;----------------------------------------------------------------------------- 1082find_file: 1083 lda #$80 ; files only 1084 jmp find_dirent 1085 1086;----------------------------------------------------------------------------- 1087; find_dir 1088; 1089; Same as find_dirent, but with directory type check 1090; 1091; * c=0: failure; sets errno 1092;----------------------------------------------------------------------------- 1093find_dir: 1094 lda #$40 ; directories only 1095 jmp find_dirent 1096 1097;----------------------------------------------------------------------------- 1098; delete_entry 1099; 1100; Delete a directory entry. Requires one of find_dirent/find_file/find_dir to 1101; be called before. 1102; 1103; C: 1= ignore read-only bit 1104; 1105; * c=0: failure; sets errno 1106;----------------------------------------------------------------------------- 1107delete_entry: 1108 set16 fat32_bufptr, cur_context + context::dirent_bufptr 1109 1110 bcs @1 ; ignore read-only 1111 1112 ldy #11 1113 lda (fat32_bufptr),y 1114 and #1 1115 beq @1 1116 1117 ; read-only file 1118 lda #ERRNO_FILE_READ_ONLY 1119 jmp set_errno 1120 1121@1: 1122 lda lfn_count 1123 beq @delete_lfn_loop 1124 1125 ; rewind to first LFN entry 1126 jsr rewind_dir_entry 1127 1128@delete_lfn_loop: 1129 lda #$E5 1130 sta (fat32_bufptr) 1131 1132 jsr save_sector_buffer 1133 bcc @ret 1134 1135 dec lfn_count 1136 bmi @end ; lfn_count + 1 iterations (#LFNs + 1 SFN) 1137 1138 add16_val fat32_bufptr, fat32_bufptr, 32 1139 cmp16_val_ne fat32_bufptr, sector_buffer_end, @delete_lfn_loop 1140 1141 lda #0 1142 jsr next_sector 1143 bcs @delete_lfn_loop 1144 rts 1145 1146@end: 1147 sec 1148@ret: 1149 rts 1150 1151;----------------------------------------------------------------------------- 1152; delete_file 1153; 1154; * c=0: failure; sets errno 1155; * does not set errno = ERRNO_FILE_NOT_FOUND! 1156;----------------------------------------------------------------------------- 1157delete_file: 1158 ; Find file 1159 jsr find_file 1160 bcs delete_file2 1161@error: 1162 rts 1163 1164delete_file2: 1165 clc ; respect read-only bit 1166 jsr delete_entry 1167 bcs @1 1168 rts 1169 1170@1: 1171 ; Unlink cluster chain 1172 set32 cur_context + context::cluster, fat32_dirent + dirent::start 1173 jmp unlink_cluster_chain 1174 1175;----------------------------------------------------------------------------- 1176; fat32_init 1177; 1178; * c=0: failure; sets errno 1179;----------------------------------------------------------------------------- 1180fat32_init: 1181 ; Clear FAT32 BSS 1182 set16_val fat32_bufptr, _fat32_bss_start 1183 lda #0 1184@1: sta (fat32_bufptr) 1185 inc fat32_bufptr + 0 1186 bne @2 1187 inc fat32_bufptr + 1 1188@2: ldx fat32_bufptr + 0 1189 cpx #<_fat32_bss_end 1190 bne @1 1191 ldx fat32_bufptr + 1 1192 cpx #>_fat32_bss_end 1193 bne @1 1194 1195 ; Make sure sector_lba is non-zero 1196 ; (was overwritten by sdcard_init) 1197 lda #$FF 1198 sta sector_lba 1199 1200 ; No current volume 1201 sta volume_idx 1202 1203 ; No time set up 1204 sta fat32_time_year 1205 1206 sec 1207 rts 1208 1209;----------------------------------------------------------------------------- 1210; mount 1211; 1212; In: a partition number (0+) 1213; 1214; * c=0: failure; sets errno 1215;----------------------------------------------------------------------------- 1216mount: 1217 pha ; partition number 1218 1219 jsr load_mbr_sector 1220 pla 1221 bcs @2a 1222@error: clc 1223 rts 1224 1225@2a: asl ; *16 1226 asl 1227 asl 1228 asl 1229 tax 1230 1231 ; Check partition type 1232 lda sector_buffer + $1BE + 4, x 1233 cmp #$0B 1234 beq @3 1235 cmp #$0C 1236 beq @3 1237 lda #ERRNO_NO_FS 1238 jmp set_errno 1239 1240@3: 1241 ; Get LBA of partition 1242 lda sector_buffer + $1BE + 8 + 0, x 1243 sta cur_volume + fs::lba_partition + 0 1244 lda sector_buffer + $1BE + 8 + 1, x 1245 sta cur_volume + fs::lba_partition + 1 1246 lda sector_buffer + $1BE + 8 + 2, x 1247 sta cur_volume + fs::lba_partition + 2 1248 lda sector_buffer + $1BE + 8 + 3, x 1249 sta cur_volume + fs::lba_partition + 3 1250 1251 ; Read first sector of partition 1252 set32 cur_context + context::lba, cur_volume + fs::lba_partition 1253 jsr load_sector_buffer 1254 bcc @error 1255 1256 ; Some sanity checks 1257 lda sector_buffer + 510 ; Check signature 1258 cmp #$55 1259 beq :+ 1260@fs_inconsistent: 1261 lda #ERRNO_FS_INCONSISTENT 1262 jmp set_errno 1263: lda sector_buffer + 511 1264 cmp #$AA 1265 bne @fs_inconsistent 1266 lda sector_buffer + 16 ; # of FATs should be 2 1267 cmp #2 1268 bne @fs_inconsistent 1269 lda sector_buffer + 17 ; Root entry count = 0 for FAT32 1270 bne @fs_inconsistent 1271 lda sector_buffer + 18 1272 bne @fs_inconsistent 1273 1274 ; Get sectors per cluster 1275 lda sector_buffer + 13 1276 sta cur_volume + fs::sectors_per_cluster 1277 beq @fs_inconsistent 1278 1279 ; Calculate shift amount based on sectors per cluster 1280 ; cluster_shift already 0 1281@4: lsr 1282 beq @5 1283 inc cur_volume + fs::cluster_shift 1284 bra @4 1285@5: 1286 ; FAT size in sectors 1287 set32 cur_volume + fs::fat_size, sector_buffer + 36 1288 1289 ; Root cluster 1290 set32 cur_volume + fs::rootdir_cluster, sector_buffer + 44 1291 1292 ; Calculate LBA of first FAT 1293 add32_16 cur_volume + fs::lba_fat, cur_volume + fs::lba_partition, sector_buffer + 14 1294 1295 ; Calculate LBA of first data sector 1296 add32 cur_volume + fs::lba_data, cur_volume + fs::lba_fat, cur_volume + fs::fat_size 1297 add32 cur_volume + fs::lba_data, cur_volume + fs::lba_data, cur_volume + fs::fat_size 1298 1299 ; Calculate number of clusters on volume: (total_sectors - lba_data) >> cluster_shift 1300 set32 cur_volume + fs::cluster_count, sector_buffer + 32 1301 sub32 cur_volume + fs::cluster_count, cur_volume + fs::cluster_count, cur_volume + fs::lba_data 1302 ldy cur_volume + fs::cluster_shift 1303 beq @7 1304@6: shr32 cur_volume + fs::cluster_count 1305 dey 1306 bne @6 1307@7: 1308 ; Get FS info sector 1309 add32_16 cur_volume + fs::lba_fsinfo, cur_volume + fs::lba_partition, sector_buffer + 48 1310 1311 ; Load FS info sector 1312 set32 cur_context + context::lba, cur_volume + fs::lba_fsinfo 1313 jsr load_sector_buffer 1314 bcs @8 1315 rts 1316@8: 1317 ; Get number of free clusters 1318 set32 cur_volume + fs::free_clusters, sector_buffer + 488 1319 1320 ; Set initial start point for free cluster search 1321 set32_val cur_volume + fs::free_cluster, 2 1322 1323 ; Success 1324 lda #$80 1325 sta cur_volume + fs::mounted 1326 sec 1327 rts 1328 1329;----------------------------------------------------------------------------- 1330; unmount 1331; 1332; In: a partition number (0+) 1333; 1334; * c=0: failure; sets errno 1335;----------------------------------------------------------------------------- 1336unmount: 1337 sec ; don't mount 1338 jsr set_volume 1339 ; Set unmounted 1340 stz cur_volume + fs::mounted 1341 ; No current volume 1342 lda #$ff 1343 sta volume_idx 1344 rts 1345 1346;----------------------------------------------------------------------------- 1347; fat32_set_context 1348; 1349; context index in A 1350; 1351; * c=0: failure; sets errno 1352;----------------------------------------------------------------------------- 1353; TODO: even in the error case, the context must always been set, otherwise 1354; we are stuck. 1355fat32_set_context: 1356 stz fat32_errno 1357 1358 ; Already selected? 1359 cmp context_idx 1360 beq @done 1361 1362 ; Valid context index? 1363 cmp #FAT32_CONTEXTS 1364 bcs @error 1365 1366.if ::FAT32_CONTEXTS > 1 1367 ; Save new context index 1368 pha 1369 1370 ; Save dirty sector 1371 jsr sync_sector_buffer 1372 bcc @error2 1373 1374 ; Put zero page variables in current context 1375 set16 cur_context + context::bufptr, fat32_bufptr 1376 1377 .assert CONTEXT_SIZE = 32, error 1378 ; Copy current context back 1379 lda context_idx ; X=A*32 1380 asl 1381 asl 1382 asl 1383 asl 1384 asl 1385 tax 1386 1387 ldy #0 1388@1: lda cur_context, y 1389 sta contexts, x 1390 inx 1391 iny 1392 cpy #(.sizeof(context)) 1393 bne @1 1394 1395 ; Copy new context to current 1396 pla ; Get new context idx 1397 sta context_idx ; X=A*32 1398 asl 1399 asl 1400 asl 1401 asl 1402 asl 1403 tax 1404 1405 ldy #0 1406@2: lda contexts, x 1407 sta cur_context, y 1408 inx 1409 iny 1410 cpy #(.sizeof(context)) 1411 bne @2 1412 1413 ; Restore zero page variables from current context 1414 set16 fat32_bufptr, cur_context + context::bufptr 1415 1416 ldx context_idx 1417 lda volume_for_context, x 1418 cmp #$ff 1419 beq @no_volume 1420 clc 1421 jsr set_volume 1422 bcc @error 1423 1424@no_volume: 1425 ; Reload sector 1426 lda cur_context + context::flags 1427 bit #FLAG_IN_USE 1428 beq @reload_done 1429 jsr load_sector_buffer 1430 bcc @error 1431@reload_done: 1432.endif 1433 1434@done: sec 1435 rts 1436@error2: 1437 pla 1438@error: clc 1439 rts 1440 1441;----------------------------------------------------------------------------- 1442; fat32_get_context 1443;----------------------------------------------------------------------------- 1444fat32_get_context: 1445 lda context_idx 1446 rts 1447 1448;----------------------------------------------------------------------------- 1449; fat32_open_dir 1450; 1451; Open current working directory 1452; 1453; * c=0: failure; sets errno 1454;----------------------------------------------------------------------------- 1455fat32_open_dir: 1456 stz fat32_errno 1457 1458 ; Check if context is free 1459 lda cur_context + context::flags 1460 bne @error 1461 1462 ; Use current directory if fat32_ptr is zero 1463 cmp16_z fat32_ptr, @cur_dir 1464 1465 ; Find directory and use it 1466 jsr find_dir 1467 bcs @1 1468 lda #ERRNO_FILE_NOT_FOUND 1469 jmp set_errno 1470 1471@1: 1472 set32 cur_context + context::cluster, fat32_dirent + dirent::start 1473 bra @open 1474 1475@cur_dir: 1476 ; Open current directory 1477 set32 cur_context + context::cluster, cur_volume + fs::cwd_cluster 1478 1479@open: jsr open_cluster 1480 bcc @error 1481 1482 ; Set context as in-use 1483 lda #FLAG_IN_USE 1484 sta cur_context + context::flags 1485 1486 ; Success 1487 sec 1488 rts 1489 1490@error: clc 1491 rts 1492 1493;----------------------------------------------------------------------------- 1494; fat32_find_dirent 1495; 1496; same args as find_dirent 1497;----------------------------------------------------------------------------- 1498fat32_find_dirent: 1499 ; Check if context is free 1500 ldx cur_context + context::flags 1501 bne @error 1502 1503 ; Open current directory 1504 jmp find_dirent 1505 1506@error: clc 1507 rts 1508 1509;----------------------------------------------------------------------------- 1510; fat32_read_dirent 1511; 1512; * c=0: failure; sets errno 1513;----------------------------------------------------------------------------- 1514fat32_read_dirent: 1515 stz fat32_errno 1516 1517 sec 1518 jmp read_dirent 1519 1520;----------------------------------------------------------------------------- 1521; read_dirent 1522; 1523; In: c=1: return next file entry 1524; c=0: return next volume label entry 1525; 1526; * c=0: failure; sets errno 1527;----------------------------------------------------------------------------- 1528read_dirent: 1529 ror 1530 sta tmp_dirent_flag 1531 stz lfn_index 1532 stz lfn_count 1533 1534@fat32_read_dirent_loop: 1535 ; Load next sector if at end of buffer 1536 cmp16_val_ne fat32_bufptr, sector_buffer_end, @1 1537 lda #0 1538 jsr next_sector 1539 bcs @1 1540@error: clc ; Indicate error 1541 rts 1542@1: 1543 ; Last entry? 1544 lda (fat32_bufptr) 1545 beq @error 1546 1547 ; Skip empty entries 1548 cmp #$E5 1549 bne @3 1550 jmp @next_entry_clear_lfn_buffer 1551@3: 1552 1553 ; Volume label entry? 1554 ldy #11 1555 lda (fat32_bufptr), y 1556 sta fat32_dirent + dirent::attributes 1557 cmp #8 1558 bne @2 1559 bit tmp_dirent_flag 1560 bpl @2b 1561@2a: 1562 jmp @next_entry 1563@2: 1564 bit tmp_dirent_flag 1565 bpl @2a 1566 1567@2b: 1568 ; check for LFN entry 1569 lda fat32_dirent + dirent::attributes 1570 cmp #$0f 1571 beq @lfn_entry 1572 bra @short_entry 1573 1574@lfn_entry: 1575 1576 ; does it have the right index? 1577 jsr check_lfn_index 1578 bcs @index_ok 1579 jmp @next_entry_clear_lfn_buffer 1580@index_ok: 1581 1582 ; first LFN entry? 1583 lda lfn_index 1584 bne @not_first_lfn_entry 1585 1586; first LFN entry 1587 ; init buffer 1588 set16_val fat32_lfn_bufptr, lfn_buf 1589 1590 ; save checksum 1591 ldy #13 1592 lda (fat32_bufptr), y 1593 sta lfn_checksum 1594 1595 ; prepare expected index 1596 lda (fat32_bufptr) 1597 and #$1f 1598 sta lfn_index 1599 sta lfn_count 1600 1601 ; add entry to buffer 1602 jsr add_lfn_entry 1603 1604 ; remember dir entry 1605 jsr mark_dir_entry 1606 1607 ; continue with next entry 1608 jmp @next_entry 1609 1610; followup LFN entry 1611@not_first_lfn_entry: 1612 1613 ; compare checksum 1614 ldy #13 1615 lda (fat32_bufptr), y 1616 cmp lfn_checksum 1617 beq @checksum_ok 1618 jmp @next_entry_clear_lfn_buffer 1619 1620@checksum_ok: 1621 dec lfn_index 1622 1623 ; add entry to buffer 1624 jsr add_lfn_entry 1625 1626 ; continue with next entry 1627 jmp @next_entry 1628 1629 1630;******* 1631@short_entry: 1632 ; is there a LFN? 1633 lda lfn_index 1634 cmp #1 1635 bne @is_short 1636 1637 ; Compare checksum 1638 lda #0 1639 tay 1640@checksum_loop: 1641 tax 1642 lsr 1643 txa 1644 ror 1645 clc 1646 adc (fat32_bufptr), y 1647 iny 1648 cpy #11 1649 bne @checksum_loop 1650 1651 cmp lfn_checksum 1652 bne @is_short 1653 1654 lda lfn_count 1655 sta lfn_index 1656 1657 ldx #0 1658@decode_lfn_loop: 1659 sub16_val fat32_lfn_bufptr, fat32_lfn_bufptr, 32 1660 1661 ldy #1 1662 lda #5 1663 jsr decode_lfn_chars 1664 bcc @name_done2 1665 ldy #14 1666 lda #6 1667 jsr decode_lfn_chars 1668 bcc @name_done2 1669 ldy #28 1670 lda #2 1671 jsr decode_lfn_chars 1672 dec lfn_index 1673 bne @decode_lfn_loop 1674@name_done2: 1675 bra @name_done ; yes, we need to zero terminate! 1676 1677@is_short: 1678 ; Volume label decoding 1679 bit tmp_dirent_flag 1680 bmi @4b 1681 jsr decode_volume_label 1682 bra @name_done_z 1683 1684@4b: ; get upper/lower case flags 1685 ldy #12 1686 lda (fat32_bufptr), y 1687 asl 1688 asl 1689 asl ; bits 7 and 6 1690 sta tmp_sfn_case 1691 1692 ; Copy first part of file name 1693 ldy #0 1694@4: lda (fat32_bufptr), y 1695 cmp #' ' 1696 beq @skip_spaces 1697 cmp #$05 ; $05 at first character translates into $E5 1698 bne @n05 1699 cpy #0 1700 bne @n05 1701 lda #$E5 1702@n05: bit tmp_sfn_case 1703 bvc @ucase1 1704 jsr to_lower 1705@ucase1: 1706 jsr filename_cp437_to_internal 1707 sta fat32_dirent + dirent::name, y 1708 iny 1709 cpy #8 1710 bne @4 1711 1712 ; Skip any following spaces 1713@skip_spaces: 1714 tya 1715 tax 1716@5: cpy #8 1717 beq @6 1718 lda (fat32_bufptr), y 1719 iny 1720 cmp #' ' 1721 beq @5 1722@6: 1723 ; If extension starts with a space, we're done 1724 lda (fat32_bufptr), y 1725 cmp #' ' 1726 beq @name_done 1727 1728 ; Add dot to output 1729 lda #'.' 1730 sta fat32_dirent + dirent::name, x 1731 inx 1732 1733 ; Copy extension part of file name 1734@7: lda (fat32_bufptr), y 1735 cmp #' ' 1736 beq @name_done 1737 bit tmp_sfn_case 1738 bpl @ucase2 1739 jsr to_lower 1740@ucase2: 1741 phx 1742 jsr filename_cp437_to_internal 1743 plx 1744 sta fat32_dirent + dirent::name, x 1745 iny 1746 inx 1747 cpy #11 1748 bne @7 1749 1750@name_done: 1751 ; Add zero-termination to output 1752 stz fat32_dirent + dirent::name, x 1753 1754@name_done_z: 1755 ; Decode mtime timestamp 1756 ldy #$16 1757 lda (fat32_bufptr), y 1758 iny 1759 ora (fat32_bufptr), y 1760 iny 1761 ora (fat32_bufptr), y 1762 iny 1763 ora (fat32_bufptr), y 1764 bne @ts1 1765 stz fat32_dirent + dirent::mtime_seconds 1766 stz fat32_dirent + dirent::mtime_minutes 1767 stz fat32_dirent + dirent::mtime_hours 1768 stz fat32_dirent + dirent::mtime_day 1769 lda #$ff ; year 2235 signals "no date" 1770 bra @ts2 1771@ts1: ldy #$16 1772 lda (fat32_bufptr), y 1773 sta tmp_timestamp 1774 and #31 1775 asl 1776 sta fat32_dirent + dirent::mtime_seconds 1777 iny 1778 lda (fat32_bufptr), y 1779 asl tmp_timestamp 1780 rol 1781 asl tmp_timestamp 1782 rol 1783 asl tmp_timestamp 1784 rol 1785 and #63 1786 sta fat32_dirent + dirent::mtime_minutes 1787 lda (fat32_bufptr), y 1788 lsr 1789 lsr 1790 lsr 1791 sta fat32_dirent + dirent::mtime_hours 1792 iny 1793 lda (fat32_bufptr), y 1794 tax 1795 and #31 1796 sta fat32_dirent + dirent::mtime_day 1797 iny 1798 lda (fat32_bufptr), y 1799 sta tmp_timestamp 1800 txa 1801 lsr tmp_timestamp 1802 ror 1803 lsr 1804 lsr 1805 lsr 1806 lsr 1807 sta fat32_dirent + dirent::mtime_month 1808 lda (fat32_bufptr), y 1809 lsr 1810@ts2: sta fat32_dirent + dirent::mtime_year 1811 1812 ; Copy file size 1813 ldy #28 1814 ldx #0 1815@8: lda (fat32_bufptr), y 1816 sta fat32_dirent + dirent::size, x 1817 iny 1818 inx 1819 cpx #4 1820 bne @8 1821 1822 ; Copy cluster 1823 ldy #26 1824 lda (fat32_bufptr), y 1825 sta fat32_dirent + dirent::start + 0 1826 iny 1827 lda (fat32_bufptr), y 1828 sta fat32_dirent + dirent::start + 1 1829 ldy #20 1830 lda (fat32_bufptr), y 1831 sta fat32_dirent + dirent::start + 2 1832 iny 1833 lda (fat32_bufptr), y 1834 sta fat32_dirent + dirent::start + 3 1835 1836 ; Save lba + fat32_bufptr 1837 set32 cur_context + context::dirent_lba, cur_context + context::lba 1838 set16 cur_context + context::dirent_bufptr, fat32_bufptr 1839 1840 ; Increment buffer pointer to next entry 1841 add16_val fat32_bufptr, fat32_bufptr, 32 1842 1843 sec 1844 rts 1845 1846@next_entry_clear_lfn_buffer: 1847 stz lfn_index 1848 1849@next_entry: 1850 add16_val fat32_bufptr, fat32_bufptr, 32 1851 jmp @fat32_read_dirent_loop 1852 1853;----------------------------------------------------------------------------- 1854; decode_volume_label 1855;----------------------------------------------------------------------------- 1856decode_volume_label: 1857 ldy #0 1858@1: lda (fat32_bufptr), y 1859 jsr filename_cp437_to_internal 1860 sta fat32_dirent + dirent::name, y 1861 iny 1862 cpy #11 1863 bne @1 1864 dey 1865 lda #$20 1866@2: cmp fat32_dirent + dirent::name, y 1867 bne @3 1868 dey 1869 bpl @2 1870@3: iny 1871 lda #0 1872 sta fat32_dirent + dirent::name, y 1873 rts 1874 1875;----------------------------------------------------------------------------- 1876; check_lfn_index 1877; 1878; * c=1: ok 1879;----------------------------------------------------------------------------- 1880check_lfn_index: 1881 lda lfn_index 1882 beq @expect_start 1883 1884 lda lfn_index 1885 dec 1886 cmp (fat32_bufptr) 1887 beq @ok 1888 1889 stz lfn_index 1890@expect_start: 1891 lda (fat32_bufptr) 1892 asl 1893 asl ; bit #6 -> C 1894 rts 1895 1896@ok: 1897 sec 1898 rts 1899 1900;----------------------------------------------------------------------------- 1901; add_lfn_entry 1902;----------------------------------------------------------------------------- 1903add_lfn_entry: 1904 ldy #31 1905: lda (fat32_bufptr), y 1906 sta (fat32_lfn_bufptr), y 1907 dey 1908 bpl :- 1909 add16_val fat32_lfn_bufptr, fat32_lfn_bufptr, 32 1910 rts 1911 1912;----------------------------------------------------------------------------- 1913; decode_lfn_chars 1914; 1915; Convert 16 bit UCS-2-encoded LFN characters to private 8 bit encoding. 1916; 1917; In: a number of characters 1918; x target index (offset in fat32_dirent + dirent::name) 1919; y source index (offset in (fat32_lfn_bufptr)) 1920; Out: x updated target index 1921; y updated source index 1922; c =0: terminating 0 character encountered 1923;----------------------------------------------------------------------------- 1924decode_lfn_chars: 1925 stx lfn_name_index 1926 sta lfn_char_count 1927@loop: 1928 lda (fat32_lfn_bufptr), y 1929 iny 1930 pha 1931 pha 1932 lda (fat32_lfn_bufptr), y 1933 iny 1934 plx 1935 pha 1936 jsr filename_char_ucs2_to_internal 1937 ldx lfn_name_index 1938 sta fat32_dirent + dirent::name, x 1939 inc lfn_name_index 1940 pla 1941 plx 1942 bne @cont 1943 tax 1944 beq @end 1945@cont: 1946 dec lfn_char_count 1947 bne @loop 1948 ldx lfn_name_index 1949 sec 1950 rts 1951@end: ldx lfn_name_index 1952 clc 1953 rts 1954 1955;----------------------------------------------------------------------------- 1956; encode_lfn_chars 1957; 1958; Convert characters in private 8 bit encoding to 16 bit UCS-2 (LFN) encoding. 1959; 1960; In: a number of characters 1961; x target index (offset in (fat32_lfn_bufptr)) 1962; y source index (offset in (fat32_ptr)) 1963; Out: x updated target index 1964; y updated source index 1965; c =0: terminating 0 character encountered 1966;----------------------------------------------------------------------------- 1967encode_lfn_chars: 1968 sta lfn_char_count 1969@loop: 1970 lda (fat32_ptr), y 1971 pha 1972 phy 1973 1974 phx 1975 jsr filename_char_internal_to_ucs2 1976 ply 1977 sta (fat32_lfn_bufptr), y 1978 iny 1979 txa 1980 sta (fat32_lfn_bufptr), y 1981 iny 1982 phy 1983 plx 1984 1985 ply 1986 pla 1987 beq @end 1988 iny 1989 dec lfn_char_count 1990 bne @loop 1991 sec 1992 rts 1993@end: clc 1994 rts 1995 1996;----------------------------------------------------------------------------- 1997; create_lfn 1998; 1999; * c=0: failure; sets errno 2000;----------------------------------------------------------------------------- 2001create_lfn: 2002 ; validate that filename is legal 2003 ldy name_offset 2004@validate_loop: 2005 lda (fat32_ptr), y 2006 beq @validate_ok 2007 jsr validate_char 2008 iny 2009 bcs @validate_loop 2010 lda #ERRNO_ILLEGAL_FILENAME 2011 jmp set_errno 2012 2013@validate_ok: 2014 ; init buffer 2015 set16_val fat32_lfn_bufptr, lfn_buf 2016 2017 lda #1 2018 sta lfn_index 2019 2020 ldy name_offset 2021 2022@create_lfn_loop: 2023 phy 2024 2025 ; create FLN template 2026 2027 ; fill remainder bytes with $FF 2028 ldy #31 2029: lda #$ff 2030 sta (fat32_lfn_bufptr), y 2031 dey 2032 bne :- 2033 2034 ; fill in special bytes 2035 ldy #0 2036 lda lfn_index 2037 sta (fat32_lfn_bufptr), y 2038 ldy #11 2039 lda #$0f 2040 sta (fat32_lfn_bufptr), y 2041 iny 2042 lda #0 2043 sta (fat32_lfn_bufptr), y 2044 ; leave checksum at offset 13 empty for now 2045 ldy #26 2046 lda #0 2047 sta (fat32_lfn_bufptr), y 2048 iny 2049 sta (fat32_lfn_bufptr), y 2050 2051 ply 2052 2053 ; put 13 chars into entry 2054 ldx #1 2055 lda #5 2056 jsr encode_lfn_chars 2057 bcc @name_done 2058 ldx #14 2059 lda #6 2060 jsr encode_lfn_chars 2061 bcc @name_done 2062 ldx #28 2063 lda #2 2064 jsr encode_lfn_chars 2065 bcc @name_done 2066 2067 add16_val fat32_lfn_bufptr, fat32_lfn_bufptr, 32 2068 2069 inc lfn_index 2070 2071 bra @create_lfn_loop 2072 2073@name_done: 2074 lda (fat32_lfn_bufptr) 2075 sta lfn_count 2076 ora #$40 2077 sta (fat32_lfn_bufptr) 2078 2079 sec 2080 rts 2081 2082;----------------------------------------------------------------------------- 2083; fat32_read_dirent_filtered 2084; 2085; Returns next dirent that matches the name/pattern in (fat32_ptr) 2086; 2087; * c=0: failure; sets errno 2088;----------------------------------------------------------------------------- 2089fat32_read_dirent_filtered: 2090 stz fat32_errno 2091 2092 jsr fat32_read_dirent 2093 bcc @error 2094 2095 cmp16_z fat32_ptr, @ok 2096 2097 ldy #0 2098 jsr match_name 2099 bcc fat32_read_dirent_filtered 2100@ok: 2101 sec 2102 rts 2103 2104@error: 2105 clc 2106 rts 2107 2108;----------------------------------------------------------------------------- 2109; fat32_chdir 2110; 2111; * c=0: failure; sets errno 2112;----------------------------------------------------------------------------- 2113fat32_chdir: 2114 stz fat32_errno 2115 2116 ; Check if context is free 2117 lda cur_context + context::flags 2118 bne @error 2119 2120 ; Find directory 2121 jsr find_dir 2122 bcs @1 2123 lda #ERRNO_FILE_NOT_FOUND 2124 jmp set_errno 2125 2126@1: 2127 ; Set as current directory 2128 set32 cur_volume + fs::cwd_cluster, fat32_dirent + dirent::start 2129 2130 sec 2131 rts 2132 2133@error: clc 2134 rts 2135 2136;----------------------------------------------------------------------------- 2137; fat32_rename 2138; 2139; * c=0: failure; sets errno 2140;----------------------------------------------------------------------------- 2141fat32_rename: 2142 stz fat32_errno 2143 2144 ; Check if context is free 2145 lda cur_context + context::flags 2146 beq @0 2147@error: clc 2148 rts 2149 2150@0: 2151 ; Save first argument 2152 set16 tmp_buf, fat32_ptr 2153 2154 ; Make sure target name doesn't exist 2155 set16 fat32_ptr, fat32_ptr2 2156 lda #0 ; allow files and directories 2157 jsr find_dirent 2158 bcc @1 2159 ; Error, file exists 2160 lda #ERRNO_FILE_EXISTS 2161 jmp set_errno 2162 2163@1: 2164 ; Find file to rename 2165 set16 fat32_ptr, tmp_buf 2166 lda #0 ; allow files and directories 2167 jsr find_dirent 2168 bcs @3 2169 lda #ERRNO_FILE_NOT_FOUND 2170 jmp set_errno 2171 2172@3: 2173 ; rescue shortname entry 2174 set16 fat32_bufptr, cur_context + context::dirent_bufptr 2175 2176 ldy #11 2177@loop: lda (fat32_bufptr), y 2178 sta tmp_entry - 11, y 2179 iny 2180 cpy #32 2181 bne @loop 2182 2183 ; delete 2184 sec ; ignore read-only bit 2185 jsr delete_entry 2186 bcs @4 2187 rts 2188@4: 2189 2190 ; target name 2191 set16 fat32_ptr, fat32_ptr2 2192 2193 ; Find space 2194 jsr find_space_for_lfn 2195 bcc @error 2196 2197 ; Create short name 2198 jsr create_shortname 2199 bcc @error 2200 2201 ; Write LFN entries 2202 jsr write_lfn_entries 2203 bcc @error 2204 2205 ; Copy new shortname into sector buffer 2206 ldy #0 2207@2: lda shortname_buf, y 2208 sta (fat32_bufptr), y 2209 iny 2210 cpy #11 2211 bne @2 2212 2213 ; restore remainder of short name entry 2214@5: lda tmp_entry - 11, y 2215 sta (fat32_bufptr), y 2216 iny 2217 cpy #32 2218 bne @5 2219 2220 ; Write sector buffer to disk 2221 jmp save_sector_buffer 2222 2223;----------------------------------------------------------------------------- 2224; fat32_set_attribute 2225; 2226; A: File attribute 2227; 2228; * c=0: failure; sets errno 2229;----------------------------------------------------------------------------- 2230fat32_set_attribute: 2231 stz fat32_errno 2232 2233 and #$ff-$10 ; clear directory bit 2234 sta tmp_buf 2235 2236 ; Check if context is free 2237 lda cur_context + context::flags 2238 beq @0 2239@error: clc 2240 rts 2241 2242@0: 2243 ; Find file 2244 lda #0 ; allow files and directories 2245 jsr find_dirent 2246 bcs @3 2247 lda #ERRNO_FILE_NOT_FOUND 2248 jmp set_errno 2249 2250@3: 2251 ; Set attribute 2252 set16 fat32_bufptr, cur_context + context::dirent_bufptr 2253 ldy #11 2254 lda (fat32_bufptr), y 2255 and #$10 ; preserve directory bit 2256 ora tmp_buf 2257 sta (fat32_bufptr), y 2258 2259 ; Write sector buffer to disk 2260 jmp save_sector_buffer 2261 2262;----------------------------------------------------------------------------- 2263; fat32_delete 2264;----------------------------------------------------------------------------- 2265fat32_delete: 2266 stz fat32_errno 2267 2268 ; Check if context is free 2269 lda cur_context + context::flags 2270 bne @error 2271 2272 jsr delete_file 2273 bcs @1 2274 lda #ERRNO_FILE_NOT_FOUND 2275 jmp set_errno 2276 2277@error: clc 2278@1: rts 2279 2280;----------------------------------------------------------------------------- 2281; fat32_rmdir 2282; 2283; * c=0: failure; sets errno 2284;----------------------------------------------------------------------------- 2285fat32_rmdir: 2286 stz fat32_errno 2287 2288 ; Check if context is free 2289 lda cur_context + context::flags 2290 beq @1 2291@error: clc 2292 rts 2293@1: 2294 ; Find directory 2295 jsr find_dir 2296 bcs @2 2297@fnf: 2298 lda #ERRNO_FILE_NOT_FOUND 2299 jmp set_errno 2300 2301@2: 2302 ; make sure user isn't trying to remove '.' or '..' entries 2303 jsr check_dot_or_dotdot 2304 bcs @fnf 2305 2306 ; Open directory 2307 set32 cur_context + context::cluster, fat32_dirent + dirent::start 2308 jsr open_cluster 2309 bcc @error 2310 2311 ; Make sure directory is empty 2312@next: jsr fat32_read_dirent 2313 bcs @3 2314 lda fat32_errno 2315 beq @done 2316 clc 2317 rts 2318 2319@3: ; Allow for '.' and '..' entries 2320 jsr check_dot_or_dotdot 2321 bcs @next 2322 lda #ERRNO_DIR_NOT_EMPTY 2323 jmp set_errno 2324 2325@done: 2326 ; Find directory 2327 jsr find_dir 2328 bcc @error 2329 2330 clc ; respect read-only bit 2331 jsr delete_entry 2332 bcs @4 2333 rts 2334 2335@4: 2336 ; Unlink cluster chain 2337 set32 cur_context + context::cluster, fat32_dirent + dirent::start 2338 jmp unlink_cluster_chain 2339 2340check_dot_or_dotdot: 2341 lda fat32_dirent + dirent::name 2342 cmp #'.' 2343 bne @no 2344 lda fat32_dirent + dirent::name + 1 2345 beq @yes 2346 cmp #'.' 2347 bne @no 2348 lda fat32_dirent + dirent::name + 2 2349 beq @yes 2350@no: clc 2351 rts 2352@yes: sec 2353 rts 2354 2355;----------------------------------------------------------------------------- 2356; fat32_open 2357; 2358; Open file specified in string pointed to by fat32_ptr 2359; 2360; * c=0: failure; sets errno 2361;----------------------------------------------------------------------------- 2362fat32_open: 2363 stz fat32_errno 2364 2365 ; Check if context is free 2366 lda cur_context + context::flags 2367 bne @error 2368 2369 ; Find file 2370 jsr find_file 2371 bcs @1 2372 lda #ERRNO_FILE_NOT_FOUND 2373 jmp set_errno 2374 2375@1: 2376 ; Open file 2377 stz cur_context + context::eof 2378 set32_val cur_context + context::file_offset, 0 2379 set32 cur_context + context::file_size, fat32_dirent + dirent::size 2380 set32 cur_context + context::start_cluster, fat32_dirent + dirent::start 2381 set32 cur_context + context::cluster, fat32_dirent + dirent::start 2382 jsr open_cluster 2383 bcc @error 2384 2385 ; Set context as in-use 2386 lda #FLAG_IN_USE 2387 sta cur_context + context::flags 2388 2389 ; Success 2390 sec 2391 rts 2392 2393@error: clc 2394 rts 2395 2396;----------------------------------------------------------------------------- 2397; find_space_for_lfn 2398; 2399; * c=0: failure; sets errno 2400;----------------------------------------------------------------------------- 2401find_space_for_lfn: 2402 ; Create LFN 2403 jsr create_lfn 2404 bcc @error 2405 2406 stz free_entry_count 2407 2408 ; Find free directory entry 2409 set32 cur_context + context::cluster, tmp_dir_cluster 2410 jsr open_cluster 2411 bcc @error 2412 2413@next_entry: 2414 ; Load next sector if at end of buffer (allocate and clear new cluster if needed) 2415 cmp16_val_ne fat32_bufptr, sector_buffer_end, @1 2416 lda #3 2417 jsr next_sector 2418 bcs @1 2419@error: clc 2420 rts 2421@1: 2422 ; Is this entry free? 2423 lda (fat32_bufptr) 2424 beq @free_entry 2425 cmp #$E5 2426 beq @free_entry 2427 2428 stz free_entry_count 2429 2430@try_next: 2431 ; Increment buffer pointer to next entry 2432 add16_val fat32_bufptr, fat32_bufptr, 32 2433 bra @next_entry 2434 2435 ; Free directory entry found 2436@free_entry: 2437 lda free_entry_count 2438 bne @not_first_free_entry 2439 2440 ; remember where the first free one was 2441 jsr mark_dir_entry 2442 2443@not_first_free_entry: 2444 lda free_entry_count 2445 inc free_entry_count 2446 cmp lfn_count 2447 bne @try_next ; not reached lfn_count+1 yet 2448 2449 ; enough consecutive entries found 2450 ; -> set pointer to first free entry 2451 jmp rewind_dir_entry 2452 2453;----------------------------------------------------------------------------- 2454; mark_dir_entry 2455; 2456; Save current cluster, LBA and directory entry index. 2457;----------------------------------------------------------------------------- 2458mark_dir_entry: 2459 ; save cluster 2460 lda cur_context + context::cluster + 0 2461 sta marked_entry_cluster + 0 2462 lda cur_context + context::cluster + 1 2463 sta marked_entry_cluster + 1 2464 lda cur_context + context::cluster + 2 2465 sta marked_entry_cluster + 2 2466 lda cur_context + context::cluster + 3 2467 sta marked_entry_cluster + 3 2468 ; save LBA 2469 lda cur_context + context::lba + 0 2470 sta marked_entry_lba + 0 2471 lda cur_context + context::lba + 1 2472 sta marked_entry_lba + 1 2473 lda cur_context + context::lba + 2 2474 sta marked_entry_lba + 2 2475 lda cur_context + context::lba + 3 2476 sta marked_entry_lba + 3 2477 ; save offset 2478 lda fat32_bufptr + 0 2479 sta marked_entry_offset + 0 2480 lda fat32_bufptr + 1 2481 sta marked_entry_offset + 1 2482 rts 2483 2484;----------------------------------------------------------------------------- 2485; rewind_dir_entry 2486; 2487; Restore cluster, LBA and directory entry index. 2488;----------------------------------------------------------------------------- 2489rewind_dir_entry: 2490 ; restore cluster 2491 lda marked_entry_cluster + 0 2492 sta cur_context + context::cluster + 0 2493 lda marked_entry_cluster + 1 2494 sta cur_context + context::cluster + 1 2495 lda marked_entry_cluster + 2 2496 sta cur_context + context::cluster + 2 2497 lda marked_entry_cluster + 3 2498 sta cur_context + context::cluster + 3 2499 ; restore LBA 2500 lda marked_entry_lba + 0 2501 sta cur_context + context::lba + 0 2502 lda marked_entry_lba + 1 2503 sta cur_context + context::lba + 1 2504 lda marked_entry_lba + 2 2505 sta cur_context + context::lba + 2 2506 lda marked_entry_lba + 3 2507 sta cur_context + context::lba + 3 2508 ; restore entry 2509 lda marked_entry_offset + 0 2510 sta fat32_bufptr + 0 2511 lda marked_entry_offset + 1 2512 sta fat32_bufptr + 1 2513 2514 ; load 2515 jmp load_sector_buffer 2516 2517;----------------------------------------------------------------------------- 2518; write_lfn_entries 2519; 2520; * c=0: failure; sets errno 2521;----------------------------------------------------------------------------- 2522write_lfn_entries: 2523 dec lfn_count 2524 bpl @1 2525 sec 2526 rts 2527 2528@1: 2529 ; Copy LFN entry 2530 ldy #31 2531@2b: lda (fat32_lfn_bufptr), y 2532 sta (fat32_bufptr), y 2533 dey 2534 bpl @2b 2535 2536 ; set checksum 2537 ldy #13 2538 lda lfn_checksum 2539 sta (fat32_bufptr), y 2540 2541 jsr save_sector_buffer 2542 bcs @ok 2543@error: 2544 clc 2545 rts 2546 2547@ok: 2548 add16_val fat32_bufptr, fat32_bufptr, 32 2549 sub16_val fat32_lfn_bufptr, fat32_lfn_bufptr, 32 2550 2551 cmp16_val_ne fat32_bufptr, sector_buffer_end, @1b 2552 lda #0 2553 jsr next_sector 2554 bcc @error 2555@1b: 2556 bra write_lfn_entries 2557 2558@write_lfn_entries_end: 2559 rts 2560 2561;----------------------------------------------------------------------------- 2562; create_dir_entry 2563; 2564; A: File attribute 2565; 2566; * c=0: failure; sets errno 2567;----------------------------------------------------------------------------- 2568create_dir_entry: 2569 sta tmp_attrib 2570 2571 ; Find space 2572 jsr find_space_for_lfn 2573 bcs @1 2574@error: 2575 clc 2576 rts 2577 2578@1: 2579 ; Create short name 2580 jsr create_shortname 2581 bcc @error 2582 2583 ; Write LFN entries 2584 jsr write_lfn_entries 2585 bcc @error 2586 2587 ; Write short name entry 2588 2589 ; Copy shortname in new entry 2590 ldy #0 2591@2: lda shortname_buf, y 2592 sta (fat32_bufptr), y 2593 iny 2594 cpy #11 2595 bne @2 2596 2597 ; File attribute 2598 lda tmp_attrib 2599 sta (fat32_bufptr), y 2600 iny 2601 2602 ; Zero fill rest of entry 2603 lda #0 2604@3: sta (fat32_bufptr), y 2605 iny 2606 cpy #32 2607 bne @3 2608 2609 ; Save lba + fat32_bufptr 2610 set32 cur_context + context::dirent_lba, cur_context + context::lba 2611 set16 cur_context + context::dirent_bufptr, fat32_bufptr 2612 2613 ; Write sector buffer to disk 2614 jsr save_sector_buffer 2615 bcc @error 2616 2617 ; Set context as in-use 2618 lda #FLAG_IN_USE 2619 sta cur_context + context::flags 2620 2621 ; Set up fat32_bufptr to trigger cluster allocation at first write 2622 set16_val fat32_bufptr, sector_buffer_end 2623 2624 sec 2625 rts 2626 2627;----------------------------------------------------------------------------- 2628; fat32_create 2629; 2630; Create file. 2631; 2632; c=1: Delete it if it already exists. 2633;----------------------------------------------------------------------------- 2634fat32_create: 2635 php ; overwrite flag 2636 stz fat32_errno 2637 2638 ; Check if context is free 2639 lda cur_context + context::flags 2640 beq @1 2641 plp ; overwrite flag 2642@error: clc 2643 rts 2644@1: 2645 ; Check if directory entry already exists? 2646 lda #0 ; allow files and directories 2647 jsr find_dirent 2648 bcs @exists 2649 plp ; overwrite flag 2650 lda fat32_errno 2651 bne @error 2652 bra @ok 2653 2654@exists: 2655 plp ; overwrite flag 2656 bcs @overwrite 2657 2658 lda #ERRNO_FILE_EXISTS 2659 jmp set_errno 2660 2661@overwrite: 2662 ; Delete file first if it exists 2663 jsr delete_file2 2664 bcc @error 2665 2666@ok: ; Create directory entry 2667 lda #0 2668 jmp create_dir_entry 2669 2670;----------------------------------------------------------------------------- 2671; fat32_mkdir 2672; 2673; * c=0: failure; sets errno 2674;----------------------------------------------------------------------------- 2675fat32_mkdir: 2676 stz fat32_errno 2677 2678 ; Check if context is free 2679 lda cur_context + context::flags 2680 bne @error 2681 2682 ; Check if directory doesn't exist yet 2683 lda #0 ; allow files and directories 2684 jsr find_dirent 2685 bcc @0 2686 lda #ERRNO_FILE_EXISTS 2687 jsr set_errno 2688 bra @error 2689 2690@0: 2691 ; Create directory entry 2692 lda #$10 2693 jsr create_dir_entry 2694 bcc @error 2695 2696 ; Allocate the cluster 2697 jsr allocate_first_cluster 2698 bcc @error 2699 jsr clear_cluster 2700 bcc @error 2701 jsr open_cluster 2702 bcs @1 2703@error: jmp error_clear_context 2704 2705@1: 2706 ; Create '.' and '..' entries 2707 ldy #0 2708 lda #' ' 2709@2: sta sector_buffer + 0, y 2710 sta sector_buffer + 32, y 2711 iny 2712 cpy #11 2713 bne @2 2714 2715 lda #'.' ; Name 2716 sta sector_buffer + 0 2717 sta sector_buffer + 32 + 0 2718 sta sector_buffer + 32 + 1 2719 2720 lda #$10 ; Directory attribute 2721 sta sector_buffer + 11 2722 sta sector_buffer + 32 + 11 2723 2724 lda cur_volume + fs::free_cluster + 0 2725 sta sector_buffer + 26 2726 lda cur_volume + fs::free_cluster + 1 2727 sta sector_buffer + 27 2728 lda cur_volume + fs::free_cluster + 2 2729 sta sector_buffer + 20 2730 lda cur_volume + fs::free_cluster + 3 2731 sta sector_buffer + 21 2732 2733 lda tmp_dir_cluster + 0 2734 sta sector_buffer + 32 + 26 2735 lda tmp_dir_cluster + 1 2736 sta sector_buffer + 32 + 27 2737 lda tmp_dir_cluster + 2 2738 sta sector_buffer + 32 + 20 2739 lda tmp_dir_cluster + 3 2740 sta sector_buffer + 32 + 21 2741 2742 ; Set sector as dirty 2743 lda cur_context + context::flags 2744 ora #FLAG_DIRTY 2745 sta cur_context + context::flags 2746 2747 jmp fat32_close 2748 2749;----------------------------------------------------------------------------- 2750; fat32_close 2751; 2752; Close current file 2753; 2754; * c=0: failure; sets errno 2755;----------------------------------------------------------------------------- 2756fat32_close: 2757 stz fat32_errno 2758 2759 lda cur_context + context::flags 2760 bne :+ 2761 jmp @done 2762: 2763 ; Write current sector if dirty 2764 jsr sync_sector_buffer 2765 bcs :+ 2766 jmp error_clear_context 2767: 2768 ; Update directory entry with new size and mdate if needed 2769 lda cur_context + context::flags 2770 bit #FLAG_DIRENT 2771 bne :+ 2772 jmp @done 2773: and #(FLAG_DIRENT ^ $FF) ; Clear bit 2774 sta cur_context + context::flags 2775 2776 ; Load sector of directory entry 2777 set32 cur_context + context::lba, cur_context + context::dirent_lba 2778 jsr load_sector_buffer 2779 bcs :+ 2780 jmp error_clear_context 2781: 2782 ; Write size to directory entry 2783 set16 fat32_bufptr, cur_context + context::dirent_bufptr 2784 ldy #28 2785 lda cur_context + context::file_size + 0 2786 sta (fat32_bufptr), y 2787 iny 2788 lda cur_context + context::file_size + 1 2789 sta (fat32_bufptr), y 2790 iny 2791 lda cur_context + context::file_size + 2 2792 sta (fat32_bufptr), y 2793 iny 2794 lda cur_context + context::file_size + 3 2795 sta (fat32_bufptr), y 2796 2797 ; Encode mtime timestamp 2798@ts1: lda fat32_time_year 2799 inc 2800 bne @ts3 2801 ; no time set up 2802 lda #0 2803 ldy #$16 2804 sta (fat32_bufptr), y 2805 iny 2806 sta (fat32_bufptr), y 2807 iny 2808 sta (fat32_bufptr), y 2809 iny 2810 sta (fat32_bufptr), y 2811 bra @ts2 2812 2813@ts3: ldy #$16 2814 lda fat32_time_minutes 2815 tax 2816 asl 2817 asl 2818 asl 2819 asl 2820 asl 2821 sta (fat32_bufptr), y 2822 lda fat32_time_seconds 2823 lsr 2824 ora (fat32_bufptr), y 2825 sta (fat32_bufptr), y 2826 iny 2827 txa 2828 lsr 2829 lsr 2830 lsr 2831 sta (fat32_bufptr), y 2832 lda fat32_time_hours 2833 asl 2834 asl 2835 asl 2836 ora (fat32_bufptr), y 2837 sta (fat32_bufptr), y 2838 iny 2839 lda fat32_time_month 2840 tax 2841 asl 2842 asl 2843 asl 2844 asl 2845 asl 2846 ora fat32_time_day 2847 sta (fat32_bufptr), y 2848 iny 2849 txa 2850 lsr 2851 lsr 2852 lsr 2853 sta (fat32_bufptr), y 2854 lda fat32_time_year 2855 asl 2856 ora (fat32_bufptr), y 2857 sta (fat32_bufptr), y 2858@ts2: 2859 2860 ; Fill creation date if empty 2861 ldy #$0e 2862 lda (fat32_bufptr), y 2863 iny 2864 ora (fat32_bufptr), y 2865 iny 2866 ora (fat32_bufptr), y 2867 iny 2868 ora (fat32_bufptr), y 2869 bne @ts4 2870 ldy #$16 2871 lda (fat32_bufptr), y 2872 ldy #$0e 2873 sta (fat32_bufptr), y 2874 ldy #$17 2875 lda (fat32_bufptr), y 2876 ldy #$0f 2877 sta (fat32_bufptr), y 2878 ldy #$18 2879 lda (fat32_bufptr), y 2880 ldy #$10 2881 sta (fat32_bufptr), y 2882 ldy #$19 2883 lda (fat32_bufptr), y 2884 ldy #$11 2885 sta (fat32_bufptr), y 2886@ts4: 2887 2888 ; Write directory sector 2889 jsr save_sector_buffer 2890 bcc error_clear_context 2891@done: 2892 clear_bytes cur_context, .sizeof(context) 2893 2894 sec 2895 rts 2896 2897;----------------------------------------------------------------------------- 2898; error_clear_context 2899; 2900; Call this instead of fat32_close if there has been an error to avoid cached 2901; writes and possible further inconsistencies. 2902;----------------------------------------------------------------------------- 2903error_clear_context: 2904 clear_bytes cur_context, .sizeof(context) 2905 clc 2906 rts 2907 2908;----------------------------------------------------------------------------- 2909; fat32_read_byte 2910; 2911; Out: a byte 2912; x =$ff: EOF after this byte 2913; c =0: success 2914; =1: failure (includes reading past EOF) 2915; errno =0: no error, or reading past EOF 2916; =ERRNO_READ: read error 2917;----------------------------------------------------------------------------- 2918fat32_read_byte: 2919 stz fat32_errno 2920 2921 ; Bytes remaining? 2922 bit cur_context + context::eof 2923 bmi @error 2924 2925 ; At end of buffer? 2926 cmp16_val_ne fat32_bufptr, sector_buffer_end, @2 2927 lda #0 2928 jsr next_sector 2929 bcc @error 2930@2: 2931 ; Increment offset within file 2932 inc32 cur_context + context::file_offset 2933 2934 ldx #0 ; no EOF 2935 cmp32_ne cur_context + context::file_offset, cur_context + context::file_size, @3 2936 ldx #$ff ; EOF 2937 stx cur_context + context::eof 2938@3: 2939 ; Get byte from buffer 2940 lda (fat32_bufptr) 2941 inc16 fat32_bufptr 2942 2943 sec ; Indicate success 2944 rts 2945 2946@error: clc 2947 rts 2948 2949;----------------------------------------------------------------------------- 2950; fat32_read 2951; 2952; fat32_ptr : pointer to store read data 2953; fat32_size (16-bit): size of data to read 2954; 2955; On return fat32_size reflects the number of bytes actually read 2956; 2957; * c=0: failure; sets errno 2958;----------------------------------------------------------------------------- 2959fat32_read: 2960 stz fat32_errno 2961 2962 set16 fat32_ptr2, fat32_size 2963 2964@again: ; Calculate number of bytes remaining in file 2965 sub32 tmp_buf, cur_context + context::file_size, cur_context + context::file_offset 2966 lda tmp_buf + 0 2967 ora tmp_buf + 1 2968 ora tmp_buf + 2 2969 ora tmp_buf + 3 2970 bne @1 2971 clc ; End of file 2972 jmp @done 2973@1: 2974 ; Calculate number of bytes remaining in buffer 2975 sec 2976 lda #<sector_buffer_end 2977 sbc fat32_bufptr + 0 2978 sta bytecnt + 0 2979 lda #>sector_buffer_end 2980 sbc fat32_bufptr + 1 2981 sta bytecnt + 1 2982 ora bytecnt + 0 ; Check if 0 2983 bne @nonzero 2984 2985 ; At end of buffer, read next sector 2986 lda #0 2987 jsr next_sector 2988 bcs @2 2989 ; No sectors left (this shouldn't happen with a correct file size) 2990 lda #ERRNO_FS_INCONSISTENT 2991 jsr set_errno 2992 sec 2993 jmp @done 2994@2: lda #2 2995 sta bytecnt + 1 2996 2997@nonzero: 2998 ; if (fat32_size - bytecnt < 0) bytecnt = fat32_size 2999 sec 3000 lda fat32_size + 0 3001 sbc bytecnt + 0 3002 lda fat32_size + 1 3003 sbc bytecnt + 1 3004 bcs @3 3005 set16 bytecnt, fat32_size 3006@3: 3007 ; if (bytecnt > 256) bytecnt = 256 3008 lda bytecnt + 1 3009 beq @4 ; <256? 3010 stz bytecnt + 0 ; 256 bytes 3011 lda #1 3012 sta bytecnt + 1 3013@4: 3014 ; if (tmp_buf - bytecnt < 0) bytecnt = tmp_buf 3015 sec 3016 lda tmp_buf + 0 3017 sbc bytecnt + 0 3018 lda tmp_buf + 1 3019 sbc bytecnt + 1 3020 lda tmp_buf + 2 3021 sbc #0 3022 lda tmp_buf + 3 3023 sbc #0 3024 bpl @5 3025 set16 bytecnt, tmp_buf 3026@5: 3027 ; Copy bytecnt bytes from buffer 3028 ldy bytecnt 3029 dey 3030 beq @6b 3031@6: lda (fat32_bufptr), y 3032 sta (fat32_ptr), y 3033 dey 3034 bne @6 3035@6b: lda (fat32_bufptr), y 3036 sta (fat32_ptr), y 3037 3038 ; fat32_ptr += bytecnt, fat32_bufptr += bytecnt, fat32_size -= bytecnt, file_offset += bytecnt 3039 add16 fat32_ptr, fat32_ptr, bytecnt 3040 add16 fat32_bufptr, fat32_bufptr, bytecnt 3041 sub16 fat32_size, fat32_size, bytecnt 3042 add32_16 cur_context + context::file_offset, cur_context + context::file_offset, bytecnt 3043 3044 ; Check if done 3045 lda fat32_size + 0 3046 ora fat32_size + 1 3047 beq @7 3048 jmp @again ; Not done yet 3049@7: 3050 sec ; Indicate success 3051 3052@done: ; Calculate number of bytes read 3053 php 3054 sub16 fat32_size, fat32_ptr2, fat32_size 3055 plp 3056 3057 rts 3058 3059;----------------------------------------------------------------------------- 3060; allocate_first_cluster 3061; 3062; * c=0: failure; sets errno 3063;----------------------------------------------------------------------------- 3064allocate_first_cluster: 3065 jsr allocate_cluster 3066 bcs @1 3067@error: rts 3068@1: 3069 ; Load sector of directory entry 3070 set32 cur_context + context::lba, cur_context + context::dirent_lba 3071 jsr load_sector_buffer 3072 bcc @error 3073 set16 fat32_bufptr, cur_context + context::dirent_bufptr 3074 3075 ; Write cluster number to directory entry 3076 ldy #26 3077 lda cur_volume + fs::free_cluster + 0 3078 sta (fat32_bufptr), y 3079 iny 3080 lda cur_volume + fs::free_cluster + 1 3081 sta (fat32_bufptr), y 3082 ldy #20 3083 lda cur_volume + fs::free_cluster + 2 3084 sta (fat32_bufptr), y 3085 iny 3086 lda cur_volume + fs::free_cluster + 3 3087 sta (fat32_bufptr), y 3088 3089 ; Write directory sector 3090 jsr save_sector_buffer 3091 bcc @error 3092 3093 ; Set allocated cluster as current 3094 set32 cur_context + context::cluster, cur_volume + fs::free_cluster 3095 ; Set allocated cluster as start cluster 3096 set32 cur_context + context::start_cluster, cur_volume + fs::free_cluster 3097 sec 3098 rts 3099 3100;----------------------------------------------------------------------------- 3101; write__end_of_buffer 3102; 3103; * c=0: failure; sets errno 3104;----------------------------------------------------------------------------- 3105write__end_of_buffer: 3106 ; Is this the first cluster? 3107 lda cur_context + context::file_size + 0 3108 ora cur_context + context::file_size + 1 3109 ora cur_context + context::file_size + 2 3110 ora cur_context + context::file_size + 3 3111 beq @first_cluster 3112 3113 ; Go to next sector (allocate cluster if needed) 3114 lda #1 3115 jmp next_sector 3116 3117@first_cluster: 3118 jsr allocate_first_cluster 3119 bcs @1 3120 rts 3121@1: 3122 ; Load in cluster 3123 jmp open_cluster 3124 3125;----------------------------------------------------------------------------- 3126; fat32_write_byte 3127; 3128; * c=0: failure; sets errno 3129;----------------------------------------------------------------------------- 3130fat32_write_byte: 3131 stz fat32_errno 3132 3133 ; At end of buffer? (preserve A) 3134 ldx fat32_bufptr + 0 3135 cpx #<sector_buffer_end 3136 bne @write_byte 3137 ldx fat32_bufptr + 1 3138 cpx #>sector_buffer_end 3139 bne @write_byte 3140 3141 ; Handle end of buffer condition 3142 pha 3143 jsr write__end_of_buffer 3144 pla 3145 bcs @write_byte 3146 rts 3147 3148@write_byte: 3149 ; Write byte 3150 sta (fat32_bufptr) 3151 inc16 fat32_bufptr 3152 3153 ; Set sector as dirty, dirent needs update 3154 lda cur_context + context::flags 3155 ora #(FLAG_DIRTY | FLAG_DIRENT) 3156 sta cur_context + context::flags 3157 3158 inc32 cur_context + context::file_offset 3159 3160 ; if (file_size - file_offset < 0) file_size = file_offset 3161 sec 3162 lda cur_context + context::file_size + 0 3163 sbc cur_context + context::file_offset + 0 3164 lda cur_context + context::file_size + 1 3165 sbc cur_context + context::file_offset + 1 3166 lda cur_context + context::file_size + 2 3167 sbc cur_context + context::file_offset + 2 3168 lda cur_context + context::file_size + 3 3169 sbc cur_context + context::file_offset + 3 3170 bpl @1 3171 set32 cur_context + context::file_size, cur_context + context::file_offset 3172@1: 3173 sec ; Indicate success 3174 rts 3175 3176;----------------------------------------------------------------------------- 3177; fat32_write 3178; 3179; fat32_ptr : pointer to data to write 3180; fat32_size (16-bit): size of data to write 3181; 3182; * c=0: failure; sets errno 3183;----------------------------------------------------------------------------- 3184fat32_write: 3185 stz fat32_errno 3186 3187 ; Calculate number of bytes remaining in buffer 3188 sec 3189 lda #<sector_buffer_end 3190 sbc fat32_bufptr + 0 3191 sta bytecnt + 0 3192 lda #>sector_buffer_end 3193 sbc fat32_bufptr + 1 3194 sta bytecnt + 1 3195 ora bytecnt + 0 ; Check if 0 3196 bne @nonzero 3197 3198 ; Handle end of buffer condition 3199 jsr write__end_of_buffer 3200 bcs @1 3201 rts 3202@1: lda #2 3203 sta bytecnt + 1 3204 3205@nonzero: 3206 ; if (fat32_size - bytecnt < 0) bytecnt = fat32_size 3207 sec 3208 lda fat32_size + 0 3209 sbc bytecnt + 0 3210 lda fat32_size + 1 3211 sbc bytecnt + 1 3212 bcs @2 3213 set16 bytecnt, fat32_size 3214@2: 3215 ; if (bytecnt > 256) bytecnt = 256 3216 lda bytecnt + 1 3217 beq @3 ; <256? 3218 stz bytecnt + 0 ; 256 bytes 3219 lda #1 3220 sta bytecnt + 1 3221@3: 3222 ; Copy bytecnt bytes into buffer 3223 ldy bytecnt 3224 dey 3225 beq @4b 3226@4: lda (fat32_ptr), y 3227 sta (fat32_bufptr), y 3228 dey 3229 bne @4 3230@4b: lda (fat32_ptr), y 3231 sta (fat32_bufptr), y 3232 3233 ; fat32_ptr += bytecnt, fat32_bufptr += bytecnt, fat32_size -= bytecnt, file_offset += bytecnt 3234 add16 fat32_ptr, fat32_ptr, bytecnt 3235 add16 fat32_bufptr, fat32_bufptr, bytecnt 3236 sub16 fat32_size, fat32_size, bytecnt 3237 add32_16 cur_context + context::file_offset, cur_context + context::file_offset, bytecnt 3238 3239 ; if (file_size - file_offset < 0) file_size = file_offset 3240 sec 3241 lda cur_context + context::file_size + 0 3242 sbc cur_context + context::file_offset + 0 3243 lda cur_context + context::file_size + 1 3244 sbc cur_context + context::file_offset + 1 3245 lda cur_context + context::file_size + 2 3246 sbc cur_context + context::file_offset + 2 3247 lda cur_context + context::file_size + 3 3248 sbc cur_context + context::file_offset + 3 3249 bpl @5 3250 set32 cur_context + context::file_size, cur_context + context::file_offset 3251@5: 3252 ; Set sector as dirty, dirent needs update 3253 lda cur_context + context::flags 3254 ora #(FLAG_DIRTY | FLAG_DIRENT) 3255 sta cur_context + context::flags 3256 3257 ; Check if done 3258 lda fat32_size + 0 3259 ora fat32_size + 1 3260 beq @6 3261 jmp fat32_write ; Not done yet 3262@6: 3263 sec ; Indicate success 3264 rts 3265 3266;----------------------------------------------------------------------------- 3267; fat32_get_free_space 3268;----------------------------------------------------------------------------- 3269fat32_get_free_space: 3270 set32 fat32_size, cur_volume + fs::free_clusters 3271 3272 lda cur_volume + fs::cluster_shift 3273 cmp #0 ; 512B cluster 3274 beq @512b 3275 3276 sec 3277 sbc #1 3278 tax 3279 cpx #0 3280 beq @done 3281@1: shl32 fat32_size 3282 dex 3283 bne @1 3284 3285@done: sec 3286 rts 3287 3288@512b: shr32 fat32_size 3289 bra @done 3290 3291;----------------------------------------------------------------------------- 3292; fat32_next_sector 3293; 3294; * c=0: failure; sets errno 3295;----------------------------------------------------------------------------- 3296fat32_next_sector: 3297 stz fat32_errno 3298 3299 lda #0 3300 jsr next_sector 3301 bcs @1 3302 rts 3303@1: 3304 add32 cur_context + context::file_offset, cur_context + context::file_offset, 512 3305 sec 3306 rts 3307 3308;----------------------------------------------------------------------------- 3309; fat32_get_offset 3310;----------------------------------------------------------------------------- 3311fat32_get_offset: 3312 set32 fat32_size, cur_context + context::file_offset 3313 sec 3314 rts 3315 3316;----------------------------------------------------------------------------- 3317; fat32_get_vollabel 3318; 3319; Get the "volume label", i.e. the name of the filesystem. 3320; 3321; Out: fat32_dirent::name name 3322; 3323; * If a directory volume label exists, it will be returned. 3324; * Otherwise, the boot sector volume label will be returned. 3325; 3326; * c=0: failure; sets errno 3327;----------------------------------------------------------------------------- 3328fat32_get_vollabel: 3329 stz fat32_errno 3330 3331 ; Check if context is free 3332 lda cur_context + context::flags 3333 bne @error 3334 3335 ; Get directory volume label 3336 jsr open_rootdir 3337 bcc @error 3338 clc 3339 jsr read_dirent 3340 bcc @no_dir_vollabel 3341 3342 sec 3343 rts 3344 3345 ; Fall back to boot sector volume label 3346@no_dir_vollabel: 3347 ; Read first sector of partition 3348 set32 cur_context + context::lba, cur_volume + fs::lba_partition 3349 jsr load_sector_buffer 3350 bcc @error 3351 3352 set16_val fat32_bufptr, (sector_buffer + $47) 3353 jsr decode_volume_label 3354 sec 3355 rts 3356 3357@error: clc 3358 rts 3359 3360;----------------------------------------------------------------------------- 3361; fat32_set_vollabel 3362; 3363; Set the "volume label", i.e. the name of the filesystem. 3364; 3365; In: fat32_ptr name 3366; 3367; * The string can be up to 11 characters; extra characters will be ignored. 3368; * Allowed characters: 3369; - Context: Windows and Mac encode it as CP437 on disk, Mac does not allow 3370; non-ASCII characters, Windows does, but converts them to uppercase. 3371; - This function allows all CP437-encodable characters, without a case 3372; change. 3373; - Non-encodable characters will cause an error. 3374; * The volume label will always be written into the boot sector. If a 3375; directory volume label exists, it will be removed. 3376; 3377; * c=0: failure; sets errno 3378;----------------------------------------------------------------------------- 3379fat32_set_vollabel: 3380 stz fat32_errno 3381 3382 ; Check if context is free 3383 lda cur_context + context::flags 3384 bne @error 3385 3386 ; Get directory volume label 3387 jsr open_rootdir 3388 bcc @error 3389 clc 3390 jsr read_dirent 3391 bcc @no_dir_vollabel 3392 3393 sec ; ignore read-only bit 3394 jsr delete_entry 3395 bcc @error 3396 3397@no_dir_vollabel: 3398 ; Read first sector of partition 3399 set32 cur_context + context::lba, cur_volume + fs::lba_partition 3400 jsr load_sector_buffer 3401 bcc @error 3402 3403 ldy #0 3404@1: lda (fat32_ptr), y 3405 beq @2 3406 jsr filename_char_internal_to_cp437 3407 beq @fn_error 3408 sta sector_buffer + $47, y 3409 iny 3410 cpy #11 3411 bne @1 3412 3413 ; pad with spaces 3414@2: cpy #11 3415 beq @3 3416 lda #$20 3417 sta sector_buffer + $47, y 3418 iny 3419 bra @2 3420 3421@3: jmp save_sector_buffer 3422 3423@fn_error: 3424 lda #ERRNO_ILLEGAL_FILENAME 3425 jmp set_errno 3426 3427@error: clc 3428 rts 3429 3430;----------------------------------------------------------------------------- 3431; load_mbr_sector 3432; 3433; Read partition table (sector 0) 3434; 3435; * c=0: failure; sets errno 3436;----------------------------------------------------------------------------- 3437load_mbr_sector: 3438 set32_val cur_context + context::lba, 0 3439 jmp load_sector_buffer 3440 3441;----------------------------------------------------------------------------- 3442; fat32_get_ptable_entry 3443; 3444; Returns a given partition table entry 3445; 3446; In: a index 3447; 3448; * c=0: failure; sets errno 3449;----------------------------------------------------------------------------- 3450fat32_get_ptable_entry: 3451 stz fat32_errno 3452 3453 cmp #$4 3454 bcs @error ; end of list 3455 3456 asl 3457 asl 3458 asl 3459 asl 3460 pha 3461 3462 jsr load_mbr_sector 3463 plx 3464 bcs @1 3465@error: clc 3466 rts 3467 3468@1: ; start LBA 3469 phx 3470 ldy #0 3471@2: lda sector_buffer + $1BE + 8, x 3472 sta fat32_dirent + dirent::start, y 3473 inx 3474 iny 3475 cpy #4 3476 bne @2 3477 plx 3478 3479 ; size 3480 phx 3481 ldy #0 3482@3: lda sector_buffer + $1BE + 12, x 3483 sta fat32_dirent + dirent::size, y 3484 inx 3485 iny 3486 cpy #4 3487 bne @3 3488 plx 3489 3490 ; type 3491 lda sector_buffer + $1BE + 4, x 3492 sta fat32_dirent + dirent::attributes 3493 3494 stz fat32_dirent + dirent::name 3495 3496 cmp #$0b 3497 beq @read_name 3498 cmp #$0c 3499 bne @done 3500 3501@read_name: 3502 ; Read first sector of partition 3503 set32 cur_context + context::lba, fat32_dirent + dirent::start 3504 jsr load_sector_buffer 3505 bcc @error 3506 3507 set16_val fat32_bufptr, (sector_buffer + $47) 3508 jsr decode_volume_label 3509 3510@done: 3511 sec 3512 rts 3513 3514;----------------------------------------------------------------------------- 3515; fat32_seek 3516; 3517; In: fat32_size: offset 3518; 3519; * c=0: failure; sets errno 3520;----------------------------------------------------------------------------- 3521fat32_seek: 3522 stz fat32_errno 3523 3524 ; Empty file: seek is a no-op 3525 lda cur_context + context::file_size + 0 3526 ora cur_context + context::file_size + 1 3527 ora cur_context + context::file_size + 2 3528 ora cur_context + context::file_size + 3 3529 bne :+ 3530 sec 3531 rts 3532: 3533 3534 ; Set file_offset = MIN(desired_offset, file_size) 3535 lda cur_context + context::file_size + 0 3536 sec 3537 sbc fat32_size + 0 3538 lda cur_context + context::file_size + 1 3539 sbc fat32_size + 1 3540 lda cur_context + context::file_size + 2 3541 sbc fat32_size + 2 3542 lda cur_context + context::file_size + 3 3543 sbc fat32_size + 3 3544 bcs @0a 3545 set32 fat32_size, cur_context + context::file_size 3546@0a: set32 cur_context + context::file_offset, fat32_size 3547 3548 ; If file_offset == file_size, set EOF flag 3549 ldx #0 ; no EOF 3550 cmp32_ne fat32_size, cur_context + context::file_size, @0c 3551 ldx #$ff ; EOF 3552@0c: stx cur_context + context::eof 3553 3554 ; Special case: bufptr == 0 && eof? 3555 ; -> Make bufptr point to $0200 of last sector 3556 ; instead of $0000 of next (non-existent) sector 3557 lda fat32_size + 0 3558 bne @a 3559 lda fat32_size + 1 3560 and #1 3561 bne @a 3562 bit cur_context + context::eof 3563 bpl @a 3564 3565 ; Make bufptr point to end of sector_buffer 3566 lda #<(sector_buffer+$200) 3567 sta cur_context + context::bufptr + 0 3568 lda #>(sector_buffer+$200) 3569 sta cur_context + context::bufptr + 1 3570 ; Decrement sector 3571 lda fat32_size + 1 3572 sec 3573 sbc #2 ; $0200 3574 sta fat32_size + 1 3575 lda fat32_size + 2 3576 sbc #0 3577 sta fat32_size + 2 3578 lda fat32_size + 3 3579 sbc #0 3580 sta fat32_size + 3 3581 bra @b 3582 3583@a: ; Extract offset within sector 3584 lda fat32_size + 0 3585 clc 3586 adc #<sector_buffer 3587 sta cur_context + context::bufptr + 0 ; temp location 3588 lda fat32_size + 1 3589 and #1 3590 adc #>sector_buffer 3591 sta cur_context + context::bufptr + 1 3592 3593@b: ; Extract sector number 3594 lda fat32_size + 1 3595 sta fat32_size + 0 3596 lda fat32_size + 2 3597 sta fat32_size + 1 3598 lda fat32_size + 3 3599 sta fat32_size + 2 3600 stz fat32_size + 3 3601 lsr fat32_size + 2 3602 ror fat32_size + 1 3603 ror fat32_size + 0 3604 3605 ; Extract sector within cluster 3606 lda cur_volume + fs::sectors_per_cluster 3607 dec 3608 and fat32_size + 0 3609 pha 3610 3611 ; Calculate cluster index 3612 ldx cur_volume + fs::cluster_shift 3613 beq @2 3614@1: lsr fat32_size + 2 3615 ror fat32_size + 1 3616 ror fat32_size + 0 3617 dex 3618 bne @1 3619 3620 ; TODO: It would be a significant optimization to fast forward from 3621 ; the current position, it is lower than the target position. 3622 3623@2: ; Go to start cluster 3624 set32 cur_context + context::cluster, cur_context + context::start_cluster 3625 3626@2a: ; Fast forward clusters 3627 lda fat32_size + 0 3628 ora fat32_size + 1 3629 ora fat32_size + 2 3630 ora fat32_size + 3 3631 beq @3 3632 3633 jsr next_cluster 3634 bcc @error1 3635 dec32 fat32_size 3636 bra @2a 3637 3638 ; 3639@3: 3640 jsr calc_cluster_lba 3641 3642 pla 3643 sta cur_context + context::cluster_sector 3644 3645 clc 3646 adc cur_context + context::lba 3647 sta cur_context + context::lba 3648 bcc @4 3649 inc cur_context + context::lba + 1 3650 bne @4 3651 inc cur_context + context::lba + 2 3652 bne @4 3653 inc cur_context + context::lba + 3 3654@4: 3655 jsr load_sector_buffer 3656 bcc @error 3657 3658 ; Set bufptr 3659 lda cur_context + context::bufptr + 0 3660 sta fat32_bufptr 3661 lda cur_context + context::bufptr + 1 3662 sta fat32_bufptr + 1 3663 3664 sec 3665 rts 3666 3667@error1: 3668 pla 3669@error: clc 3670 rts 3671