;---------------------------------------------------------;
; FAT16 file system
;---------------------------------------------------------;
file_init:
clr T14L;0
stsi CurrSect0+2, -1 ;Read MBR to Buff0
ldiw A, 0 ;
ldi BH, 0 ;
rcall file_get_sect0 ;
rjne fsi_err ;/
inc T14L;1
ldiw Y, Buff0+0x1C2 ;Chekc MBR sign (55AA)
lddw A, Y+60 ;
subiw A, 0xAA55 ;
rjne mmi_err ;/
inc T14L;2
ld AL, Y ;Check if 1st patition is FAT16.
cpi AL, 0x06 ;
rjne fsi_err ;/
inc T14L;3
lddw A, Y+4 ;Read PBR to Buff0
ldd BL, Y+6 ;
movw CL, AL ;
mov BH, BL ;
subiw C, -1 ;
sbci BH, -1 ;
stsw FatBase+0, C ;
sts FatBase+2, BH ;
rcall file_get_sect0 ;
rjne fsi_err ;/
inc T14L;4
ldsw A, Buff0+0x1FE ;Check PBR sign (55AA)
subiw A, 0xAA55 ;
brne fsi_err ;/
inc T14L;5
ldiw Z, fsi_fat16*2 ;Check FAT16 sign
ldiw X, Buff0+0x36 ;
lpm AL, Z+ ;
ld AH, X+ ;
cp AL, AH ;
brne fsi_err ;
cpi AH, ' ' ;
brne PC-5 ;/
inc T14L;6
ldiw Y, Buff0 ;Get FAT16 information
ldd AL, Y+0x0D ;
sts ClstSize, AL ;
ldsw A, FatBase+0 ;
lds BL, FatBase+2 ;
lddw X, Y+0x16 ;
lslw X ;
addw A, X ;
adc BL, _0 ;
stsw DirBase+0, A ;
sts DirBase+2, BL ;
lddw X, Y+0x11 ;
stsw DirNum, X ;
adiw XL, 15 ;
ldi CL, 4 ;
lsrw X ;
dec CL ;
brne PC-3 ;
addw A, X ;
adc BL, _0 ;
stsw DataBase+0, A ;
sts DataBase+2, BL ;/
sez
fsi_err:
ret
fsi_fat16: .db "FAT16 "
;---------------------------------------------------------;
; Open File
;
; Call: Y = Pointer to new file structure (18 bytes)
; A = Pointer to file access buffer (512 bytes)
; Z = Pointer to file name (ASCIZ)
; Ret: eq: Y == NULL : no file
; Y != NULL : successful
; ne: error
file_open:
movw T2L, ZL ;File name
stdw Y+16, A ;File buffer
ldiw D, 0 ;Reset file search
fo_ml:
rcall file_dir_init
movw ZL, T2L ;Pick a segment
rcall file_getseg;
movw T2L, ZL ;/
cpi CL, 1 ;Is file?
brcs fo_fl ;/
brne fo_nf ;Is invalid name?
fo_dl:
rcall file_dir_next;Get next directory entry
brne fo_ret ;/
cpw Z, _0 ;No more file?
breq fo_ret ;/
rcall fo_comp ;Name matched?
brne fo_dl ;/
ldd AL, Z+11;Is not a sub-dir?
sbrs AL, 4 ;
rjmp fo_nf ;/
lddw D, Z+26 ;Follow sub-dir
rjmp fo_ml ;/
fo_fl:
rcall file_dir_next;Get next directory entry
brne fo_ret ;/
cpw Z, _0 ;No more file?
breq fo_nf ;/
rcall fo_comp ;Name matched
brne fo_fl ;/
ldd AL, Z+11;Is not a file?
andi AL, 0b00011000;
brne fo_nf ;/
stdw Y+0, _0 ; File ptr
stdw Y+2, _0 ; /
lddw A, Z+28 ; File size
lddw B, Z+30 ;
stdw Y+4, A ;
stdw Y+6, B ; /
lddw D, Z+26 ; Cluster
stdw Y+14, D ;
stdw Y+12, D ; /
rcall file_clst2sect; Sector
stdw Y+8, A ;
stdw Y+10, B ;//
sez
ret
fo_nf: sez
fo_ret: ldiw Y, 0
ret
fo_comp:
pushw Z
ldiw X, FileName;Compare file name
ldi CL, 11 ;
ld AL, Z+ ;
ld AH, X+ ;
cp AL, AH ;
brne PC+3 ;
dec CL ;
brne PC-5 ;/
popw Z
ret
;---------------------------------------------------------;
; Read a line
;
; Call: Y = Pointer to the file srtucture
; Z = Pointer to read buffer
; Ret: eq:successful
; ne:error or no more data
file_gets:
ldiw C, 1
rcall file_read
brne fg_end
cpi CL, 1
brne fg_end
ld AL, Z
cpi AL, 10
breq fg_end
cpi AL, ' '
brcs file_gets
adiw ZL, 1
rjmp file_gets
fg_end:
st Z, _0
ret
;---------------------------------------------------------;
; Read File (Byte Oriented)
;
; Call: Y = Pointer to the file srtucture
; Z = Pointer to read buffer
; C = Number of bytes to read
; Ret: eq:successful, C = Number of bytes read
; ne:error, C = Number of bytes read
file_read:
pushw Z
lddw T0, Y+0 ;T2:T0 = File ptr
lddw T2, Y+2 ;/
lddw A, Y+4 ;B:A = File size
lddw B, Y+6 ;/
subw A, T0 ;B:A -= T2:T0
sbcw B, T2 ;/
cpw A, C ;if(B:A < C) C = B:A
cpcw B, _0 ;
brcc PC+2 ;
movw CL, AL ;/
movw T4L, CL ;T4 = C
clrw C ;C = 0
lddw B, Y+16 ;B = File buffer
movw XL, T0L ;X = File ptr & 511 + B
andi XH, high(511);
addw X, B ;/
cpw X, B
breq PC+2
addi BH, high(512);B += 512
fsr_lp1:
cpw C, T4 ;if(T4 >= C) return
brcc fsr_ret ;/
cpw X, B ;if(X >=

brcs fsr_s1 ;/
lddw A, Y+8 ;Fill buffer
ldd BL, Y+10;
lddw X, Y+16 ;
rcall mmc_read_sect;
brne fsr_ret ;/
rcall file_next_sect;Get next sector number
lddw X, Y+16 ;X = File buffer
movw BL, XL ;B = X + 512
addi BH, high(512);/
fsr_s1: ld AL, X+ ;*Z++ = *X++
st Z+, AL ;/
addiw C, 1 ;C++
adcw T0, _0 ;T2:T0++
adcw T2, _0 ;/
rjmp fsr_lp1
fsr_err:
clz
fsr_ret:
stdw Y+0, T0
stdw Y+2, T2
popw Z
ret
;---------------------------------------------------------;
; Read File (Non-Bufferd, Block Oriented)
;
; Call: Y = Pointer to the file srtucture
; Z = Pointer to read buffer
; C = Number of bytes to read (must be multiple of 512)
; Ret: eq:successful, C = Number of bytes read
; ne:error, C = Number of bytes read
file_read_block:
pushw Z
lddw T0, Y+0 ;T2:T0 = File ptr
lddw T2, Y+2 ;/
movw AL, CL ;Check count varidity
andi AH, high(511);
or AL, AH ;
brne fsb_err ;/
lddw A, Y+4 ;B:A = File size
lddw B, Y+6 ;/
subw A, T0 ;B:A -= T2:T0
sbcw B, T2 ;/
brcs fsb_nd ;No more data?
breq fsb_nd ;/
cpw A, C ;if(B:A < C) C = B:A
cpcw B, _0 ;
brcc PC+2 ;
movw CL, AL ;/
movw T4L, CL ;T4 = C
clrw C ;C = 0
fsb_lp1:
cpw C, T4 ;if(T4 >= C) return
brcc fsb_ret ;/
lddw A, Y+8 ;Load block
ldd BL, Y+10;
movw XL, ZL ;
rcall mmc_read_sect;
brne fsb_err ;/
rcall file_next_sect;Get next sector number
ldi AH, high(512);
add CH, AH ;C += 512
add ZH, AH ;Z += 512
add T0H, AH ;T2:T0 += 512
adcw T2, _0 ;/
rjmp fsb_lp1
fsb_ret:
movw CL, T4L
sez
fsb_err:
stdw Y+0, T0
stdw Y+2, T2
popw Z
ret
fsb_nd:
subw C, C
popw Z
ret
;---------------------------------------------------------;
; Seek File Pointer
;
; Call: Y = Pointer to file structure
; B:A = File Pointer
; Ret: eq: successful
; ne: error
file_seek_block:
clr AL
andi AH, ~high(511)
file_seek:
lddw T0, Y+4 ;Check if size <= fp
lddw T2, Y+6 ;
cpw A, T0 ;
cpcw B, T2 ;
brcc fs_ret ;/
movw T0L, AL ;T2:T0 = File pointer
movw T2L, BL ;/
stdw Y+0, T0 ;File Pointer
stdw Y+2, T2 ;/
lddw D, Y+14 ;Cluseter
stdw Y+12, D ;/
rcall file_clst2sect;Sector
stdw Y+8, A ;
stdw Y+10, B ;/
ldiw C, 512 ;Seek sector unit
fs_lp1: cpw T0, C ;
cpcw T2, _0 ;
brcs fs_s1 ;
rcall file_next_sect;
subw T0, C ;
sbcw T2, _0 ;
rjmp fs_lp1 ;/
fs_s1:
cpw T0, _0 ;Fill buffer if needed
breq fs_ret ;
lddw A, Y+8 ;
ldd BL, Y+10;
lddw X, Y+16 ;
rcall mmc_read_sect;
brne PC+3 ;
rcall file_next_sect;/
sez
fs_ret:
ret
;---------------------------------------------------------;
; Create file name in directory entry
;
;Call: Z = Pointer to file name
;Ret: CL = 0:File, 1:Dir, 2:Invalid
; Z = Next segment
; X = Broken
file_getseg:
ldiw X, FileName;Fill output buffer
ldi CH, 11 ;
ldi CL, ' ' ;
st X+, CL ;
dec CH ;
brne PC-2 ;
sbiw XL, 11 ;/
fcv_lp:
rcall fcv_getc
cpi CL, 3
brcc PC+2
ret
cpi CH, 11 ;Buffer full?
brcc fcv_lp ;/
cpi CL, '.' ;Is a dot?
breq PC+4 ;/
st X+, CL ;Store a char
inc CH ;
rjmp fcv_lp ;/
cpi CH, 0 ;Move to extension
brne PC+3 ;
ldi CL, 2 ;
ret ;
cpi CH, 8 ;
brcc fcv_lp ;
adiw XL, 1 ;
inc CH ;
rjmp PC-4 ;/
fcv_getc:
ld CL, Z+ ;Get a char
rcall caps ;/
cpi CL, 0x7F
brcc fcv_err
cpi CL, '|'
breq fcv_err
brcc fcv_ret
cpi CL, '\'
breq fcv_err
cpi CL, '@'
brcc fcv_ret
cpi CL, ':'
brcc fcv_err
cpi CL, '0'
brcc fcv_ret
cpi CL, '/'
breq fcv_dir
cpi CL, '-'
brcc fcv_ret
cpi CL, '*'
brcc fcv_err
cpi CL, '!'
brcc fcv_ret
cpi CL, ' '
brcs fcv_file
fcv_err:ldi CL, 2
fcv_ret:ret
fcv_dir:ldi CL, 1
ret
fcv_file:ldi CL, 0
ret
;---------------------------------------------------------;
; Initialize Directory Search
;
; Call: D: <2:Root, >=2:Sub(cluster#)
file_dir_init:
ldiw A, -1
stsw DirIndex, A
stsw DirClst, D
ldsw A, DirBase+0
lds BL, DirBase+2
ldi BH, 0
cpw D, _0
breq PC+2
rcall file_clst2sect
stsw DirSect+0, A
stsw DirSect+2, B
ret
;---------------------------------------------------------;
; Get Next Directory Entry
;
; Ret: Z != NULL: successrul, Z=Pointer to the directory entry
; Z == NULL: no more entry
file_dir_next:
pushw T2
ldsw T2, DirIndex
fnd_next:
sec ;T2++
adcw T2, _0 ;/
cpw T2, _0 ;First search?
breq fnd_cmp ;/
mov AL, T2L ;Sector changed?
andi AL, 15 ;
brne fnd_cmp ;/
ldsw A, DirSect+0;Next sector
ldsw B, DirSect+2;
sec ;
adcw A, _0 ;
adc BL, _0 ;/
ldsw D, DirClst;Is root dir?
subiw D, 2 ;
brcs fnd_st ;/
inc BH ;Cluster not changed?
lds DL, ClstSize;
cp BH, DL ;
brcs fnd_st ;/
ldsw D, DirClst;Goto next cluster (sub-dir)
rcall file_next_clust;
brne fnd_ret ;
stsw DirClst, D;
rcall file_clst2sect;/
fnd_st: stsw DirSect+0, A
stsw DirSect+2, B
fnd_cmp:
ldsw A, DirClst;Is end of sub-dir?
movw BL, AL ;
subiw A, 0xFFF0;
brcc fnd_nofile;/
subiw B, 2 ;Is sub-dir?
brcc PC+8 ;/
ldsw A, DirNum;End of root-dir?
cpw T2, A ;
brcc fnd_nofile;/
ldsw A, DirSect+0;Get directory sector
lds BL, DirSect+2;
rcall file_get_sect0;
brne fnd_ret ;/
ldi AL, 32 ;Z = ((T2 & 15) << 5) + Buff0
mov AH, T2L ;
andi AH, 15 ;
mul AL, AH ;
movw ZL, T0L ;
addiw Z, Buff0;/
ldd AL, Z+0 ;Is empty entry?
cpi AL, 0xE5;
rjeq fnd_next;/
cpi AL, 0 ;Is end of dir?
breq fnd_nofile;/
sez
rjmp fnd_ret
fnd_nofile:
subw Z, Z
fnd_ret:
stsw DirIndex, T2
popw T2
ret
;---------------------------------------------------------;
; Get Next Sector
;
; Call: Y = File structure
; Ret: BL:A(!=-1) = next sector
; BL:A = -1: error
file_next_sect:
lddw A, Y+8 ;BL:A = CurrSect
lddw B, Y+10 ;BH = CurrSectInClust
subiw A, -1 ;BL:A++
sbci BL, -1 ;/
inc BH ;BH++
lds DL, ClstSize;if(BH < ClstSize) goto fsr_s1
cp BH, DL ;
brcs fsn_ret ;/
lddw D, Y+12 ;Get next cluster
rcall file_next_clust;/
brne fsn_err ;/
stdw Y+12, D ;Current cluster = D
rcall file_clst2sect;BL:A = sector#
fsn_ret:
stdw Y+8, A
stdw Y+10, B
ret
fsn_err:
ldiw A, 0xFFFF
ldiw B, 0x00FF
rjmp fsn_ret
;---------------------------------------------------------;
; Cluster --> Next cluster
;
; Call: D = Cluster
; Ret: EQ: D = Next cluster
; NE: error
file_next_clust:
ldsw A, FatBase+0;BL:A = (Current cluster / 256) + FatBase
lds BL, FatBase+2;
add AL, DH ;
adc AH, _0 ;
adc BL, _0 ;/
push DL ;Read FAT
rcall file_get_sect0;
pop XL ;
brne fnc_err ;
clr XH ;D = Buff0[(Current cluster & 255) << 1]
lslw X ;
addiw X, Buff0;
ldw D, X+ ;/
sez
fnc_err:
ret
;---------------------------------------------------------;
; Cluster --> Sector
;
; Call: D = Cluster#
; Ret: EQ: valid, BL:A=Sector#, BH=0
; NE: invalid, BL:A=-1, BH=0
; Broken: X, D, T0
file_clst2sect:
ldiw A, 0xFFF0;Check if the cluster number is valid
cpw D, A ;
brcc flc_ivd ;
subiw D, 2 ;
brcs flc_ivd ;/
movw XL, T0L ;BL:A = (D - 2) * ClstSize
lds BH, ClstSize;
mul DL, BH ;
movw AL, T0L ;
mul DH, BH ;
add AH, T0L ;
ldi BL, 0 ;
adc BL, T0H ;
movw T0L, XL ;/
ldsw X, DataBase+0;BL:A += DataBase
lds BH, DataBase+2;
addw A, X ;
adc BL, BH ;/
clr BH ;BH = 0
ret
flc_ivd:
ldiw A, 0xFFFF
ldiw B, 0x00FF
clz
ret
;---------------------------------------------------------;
; Read a sector into Buff0
file_get_sect0:
lds DL, CurrSect0+0
cp DL, AL
lds DL, CurrSect0+1
cpc DL, AH
lds DL, CurrSect0+2
cpc DL, BL
breq PC+14
stsw CurrSect0+0, A
sts CurrSect0+2, BL
ldiw X, Buff0
rcall mmc_read_sect
breq PC+4
stsi CurrSect0+2, -1
ret
;-----------------------------------------------------------;
; MMC Control
;-----------------------------------------------------------;
mmc_init:
ldi CL, 20 ;Dummy clock
ldi DL, 0xFF ;
rcall mmc_xmitbyte ;
dec CL ;
brne PC-3 ;/
clr T14L;0
ldi CL, CMD0 ;Send CMD0
ldiw B, 0 ;
ldiw A, 0 ;
rcall mmc_xmit_cmd ;
andi AL, 0b01111110 ;Check error
brne mmi_err ;/
inc T14L;1
ldi CL, CMD1 ;Send CMD1
rcall mmc_xmit_cmd ;
breq PC+3 ;Check if card is ready
andi AL, 0b01111110 ;Check error
breq PC-4 ;/
inc T14L;2
ldiw Y, Buff0 ;Read CSD, CID and OCR
movw XL, YL ;
rcall mmc_read_info ;
brne mmi_err ;/
inc T14L;3
ldd AL, Y+5 ;Check block size (512)
andi AL, 0x0F ;
cpi AL, 9 ;/
mmi_err:
sbi PORTA, MCCS ;CS=H
ret
mmc_read_sect:
pushw C
mov BH, BL ;B:A *= 512;
mov BL, AH ;
mov AH, AL ;
clr AL ;
lsl AH ;
rolw B ;/
ldi CL, CMD17 ;Send CMD17
rcall mmc_xmit_cmd ;
brne PC+6 ;/
rcall mmc_wait_data ;Wait for data token
brne PC+4 ;/
ldi CL, 0 ;Read sector
rcall mmc_rcvblock ;/
sez
popw C
sbi PORTA, MCCS ;CS=H
ret
mmc_read_info:
ldi CL, CMD9 ;Send CMD9 (Read CSD)
rcall mmc_xmit_cmd ;
brne mri_err ;/
rcall mmc_wait_data ;Wait for data token
brne mri_err ;/
ldi CL, 16/2 ;Read CSD and show
rcall mmc_rcvblock ;/
ldi CL, CMD10 ;Send CMD10 (Read CID)
rcall mmc_xmit_cmd ;
brne mri_err ;/
rcall mmc_wait_data ;Wait for data token
brne mri_err ;/
ldi CL, 16/2 ;Read CID and show
rcall mmc_rcvblock ;/
ldi CL, CMD58 ;Send CMD58 (Read OCR)
rcall mmc_xmit_cmd ;
brne mri_err ;/
ldi CL, 4 ;Read OCR
rcall mmc_rcvbyte ;
st X+, AL ;
dec CL ;
brne PC-3 ;/
mri_err:
sbi PORTA, MCCS ;CS=H
ret
mmc_xmit_cmd:
ldi DL, 0xFF ;Dummy clock
rcall mmc_xmitbyte ;/
cbi PORTA, MCCS ;CS=L
mov DL, CL ;Cmd
rcall mmc_xmitbyte ;/
mov DL, BH ;Arg
rcall mmc_xmitbyte ;
mov DL, BL ;
rcall mmc_xmitbyte ;
mov DL, AH ;
rcall mmc_xmitbyte ;
mov DL, AL ;
rcall mmc_xmitbyte ;/
ldi DL, 0x95 ;Crc
rcall mmc_xmitbyte ;/
ldi AH, 200
rcall mmc_rcvbyte ;Receive a byte
subi AH, 1 ;Timeout (375us) occured?
brcs PC+4 ;/
sbrc AL, 7 ;Is valid response?
rjmp PC-4 ;/
tst AL ;Check error status
ret
mmc_xmitbyte:; 5.5us
in DH, PORTA
ldi EL, 8
cbr DH, (1<<MCDI)+(1<<MCCK)
lsl DL
brcc PC+2
sbr DH, (1<<MCDI)
out PORTA, DH;DI=data, CK=L
sbr DH, (1<<MCCK)
out PORTA, DH;CK=H
dec EL
brne PC-8
ret
mmc_wait_data:
pushw X
ldiw X, 5333
rcall mmc_rcvbyte ;Receive a byte
sbiw XL, 1 ;Timeout (10ms) occured?
brcs PC+4 ;/
cpi AL, 0xFF ;Any token is detected?
breq PC-4 ;/
cpi AL, 0xFE ;Valid data taken ?
popw X
ret
mmc_rcvbyte:; 1.875us
in DH, PORTA
sbr DH, (1<<MCDI);DI=H
mov DL, DH
cbr DH, (1<<MCCK);CK=L
mmc_rcvbyte2:; 1.625us
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
rjmp PC+1
in AL, PINC
ret
mmc_rcvblock:; words * 2.69us + 4.1us
in DH, PORTA
sbr DH, (1<<MCDI);DI=H
mov DL, DH
cbr DH, (1<<MCCK);CK=L
mr_lp1:
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
rjmp PC+1
in AL, PINC
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
out PORTA, DH
out PORTA, DL
st X+, AL
in AL, PINC
st X+, AL
dec CL
brne mr_lp1
;mmc_rcvcrc2:
rcall mmc_rcvbyte2 ;Purge CRC
rcall mmc_rcvbyte2 ;/
sbi PORTA, MCCS ;CS=H
ret