📄 fat32.s
字号:
CalcNextCluster_SDS:
;* SeekDrive state - Execute here for the first state - SeekDrive
;* This state simply makes sure that SeekSector is idle before proceeding
lds temp,SEEKSECT_ST
cpi temp,SeekSect_ISS ; is SeekSector idle?
breq CalcNextC_SDSAA ; yes - branch forward
; print debug message if SeekSector not idle, because it certainly should be
ldi param1,'C'
call UART_TxCharWait
ldi param1,'N'
call UART_TxCharWait
ldi param1,'C'
call UART_TxCharWait
ldi param1,'-'
call UART_TxCharWait
ldi param1,'S'
call UART_TxCharWait
ldi param1,'1'
call UART_TxCharWait
ldi param1,'\n'
call UART_TxCharWait
ldi param1,'\r'
call UART_TxCharWait
; end debug code
ldi temp,SeekSect_ISS
sts SEEKSECT_ST,temp ; force SeekSector into idle state
ldi param1,CalNxCl_Busy ; say we're busy
ret ; and return to check again next time
; execute here if SeekSector is idle
CalcNextC_SDSAA:
ldi temp,CalNxCl_SDS2
sts CALNXCL_ST,temp ; next state is CalcNextCluster2
ldi param1,CalNxCl_Busy ; (say we're busy - just in case you want to return)
jmp CalcNextCluster_SDS2 ; might as well jump to the next state now
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
CalcNextCluster_SDS2:
;* SeekDrive2 state - Execute here for the second state - SeekDrive2
; Pull current cluster number off stack (32 bit value)
in ZL,SPL
in ZH,SPH
adiw ZL,3 ; Z points to low byte of cluster number
ld r0,Z+ ; r0 contains low byte
ld r1,Z+
ld r2,Z+
ld r3,Z+ ; r3 contains high byte of cluster number
; IF current_cluster >= CALNXCL_CLUS_LO AND current_cluster <= CALNXCL_CLUS_HI
; THEN return current_cluster + 1 (via the stack)
; (note that for the second code paragraph below, 2 conditional branch instructions were
; required because the AVR doesn't have a branch-if-higher instruction. Bummer. )
lds temp,CALNXCL_CLUS_LO ; get the low byte
cp r0,temp ; we will do a compare, as:
lds temp,CALNXCL_CLUS_LO+1 ; current_cluster - CALNXCL_CLUS_LO, and
cpc r1,temp ; we'll branch forward
lds temp,CALNXCL_CLUS_LO+2 ; if current_cluster < CALNXCL_CLUS_LO
cpc r2,temp
lds temp,CALNXCL_CLUS_LO+3
cpc r3,temp ; finish with the high byte
brlo CNC_SD_2 ; branch for "cache miss" if current_cluster < CALNXCL_CLUS_LO
lds temp,CALNXCL_CLUS_HI ; get the low byte
cp r0,temp ; we will do a compare, as:
lds temp,CALNXCL_CLUS_HI+1 ; current_cluster - CALNXCL_CLUS_HI, and
cpc r1,temp ; we'll branch forward for a "cache miss" if...
lds temp,CALNXCL_CLUS_HI+2 ; if current_cluster > CALNXCL_CLUS_HI
cpc r2,temp
lds temp,CALNXCL_CLUS_HI+3
cpc r3,temp ; finish with the high byte
breq CNC_SD_1 ; branch for "cache hit" if current_cluster == CALNXCL_CLUS_HI
brsh CNC_SD_2 ; branch for "cache miss" if current_cluster > CALNXCL_CLUS_HI
; execute here if we had a "cache hit"; ie, we know that for the current cluster number, the
; next cluster is simply the current cluster plus 1. So, add 1 to the current cluster number,
; put the result on the stack and return "done".
CNC_SD_1:
ldi temp,1 ; add 1 to the low byte of the current cluster number
add r0,temp
ldi temp,0
adc r1,temp ; then move the carry flag through the high 3 bytes
adc r2,temp
adc r3,temp
st -Z,r3 ; Z currently points one byte past the previous r3 read
st -Z,r2 ; so store the new cluster number back in reverse order
st -Z,r1 ; onto the stack
st -Z,r0
ldi param1,CalNxCl_Done
ret ; and finally, return with "done"
; execute here if we had a "cache miss" and hence we need to read from the drive
; Calculate offset (drive reads) into sector = remainder(cluster/128) x 2
; and store result in CALNXCL_OF
CNC_SD_2:
mov temp,r0 ; get low byte of cluster number
andi temp,0b01111111 ; we only want the low 7 bits - this is Remainder(cluster/128)
lsl temp ; multiply by 2
ldi ZL,lo8(CALNXCL_OF)
ldi ZH,hi8(CALNXCL_OF) ; Z points to CALNXCL_OF
st Z,temp ; store calculation result in CALNXCL_OF
; Calculate FAT sector number = (Cluster/128) + FAT_START_SEC
; note that dividing by 128 is a shift right 7 times
ldi temp,7 ; count of how many times to shift
CalcNextCluster_1:
lsr r3 ; shift high byte to the right
ror r2
ror r1
ror r0 ; shift right through carry
dec temp
brne CalcNextCluster_1 ; loop back if we have more shifts to do
; now we need to add FAT_START_SEC to our results so far which are in r3:r0
ldi ZL,lo8(FAT_START_SEC)
ldi ZH,hi8(FAT_START_SEC) ; Z points to FAT_START_SEC low byte
ld temp,Z+ ; temp contains FAT_START_SEC low byte
add r0,temp ; add the low bytes
ld temp,Z ; get FAT_START_SEC high byte without affecting carry flag
adc r1,temp ; add it in with carry
ldi temp,0
adc r2,temp
adc r3,temp ; add the carry in through the high bytes
; Command drive to seek to that sector (which is in r3:r0)
push r3
push r2
push r1
push r0 ; push sector number onto stack
ldi param1,1 ; we only want a single sector
rcall SeekSector ; start the SeekSector state machine running
pop r0
pop r1
pop r2
pop r3 ; clean up stack & restore sector number
; store the sector number (32 bits) for use by the next task
ldi ZL,lo8(CALNXCL_SN)
ldi ZH,hi8(CALNXCL_SN) ; point Z to sector number variable
st Z+,r0 ; store low byte first
st Z+,r1
st Z+,r2
st Z,r3 ; store high byte last
; Change state variable to GetCluster
ldi ZL,lo8(CALNXCL_ST)
ldi ZH,hi8(CALNXCL_ST) ; point Z to CalcNextCluster state variable
ldi temp,CalNxCl_GCS
st Z,temp ; state variable now contains GetCluster
; Write CalNxCl_Busy into param1 and return
ldi param1,CalNxCl_Busy
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; GetCluster state - Execute here for the second (GetCluster) state
CalcNextCluster_GCS:
; If drive not seeked yet then return with CalNxCl_Busy in param1
; Note that SeekSector requires the sector number parameter via the stack
ldi ZL,lo8(CALNXCL_SN)
ldi ZH,hi8(CALNXCL_SN) ; point Z to sector number variable
ld r0,Z+ ; read low byte first
ld r1,Z+
ld r2,Z+
ld r3,Z ; read high byte last
push r3 ; push high byte first
push r2
push r1
push r0
ldi param1,1 ; we only want a single sector
rcall SeekSector
pop temp
pop temp
pop temp
pop temp ; clean up the stack
cpi param1,SeekSect_Done
breq CalcNextCluster_2 ; branch forward if SeekSector done
ldi param1,CalNxCl_Busy
ret ; otherwise return saying Busy
; Perform CALNXCL_OF reads of the drive (and throw away the data)
CalcNextCluster_2:
ldi ZL,lo8(CALNXCL_OF)
ldi ZH,hi8(CALNXCL_OF) ; Z points to CALNXCL_OF (8 bit value)
ld temp2,Z ; temp2 is count of dummy reads required
CalcNextCluster_3:
cpi temp2,0 ; are we done doing dummy reads?
breq CalcNextCluster_4 ; branch if we're done with the dummy reads
ldi param1,IDERD_DATA ; do a read of the drive's data register
call IDE_read16 ; and ignore the result
dec temp2
brne CalcNextCluster_3 ; loop back if more dummy reads to do
; Read the next 4 bytes (2 16-bit drive reads) - this is cluster number
; We'll get them low byte to high byte - store in r3:r0 (r0 is low byte)
CalcNextCluster_4:
ldi param1,IDERD_DATA ; do a read of the drive's data register
call IDE_read16
mov r0,param1 ; low byte of cluster number in r0
mov r1,param2
ldi param1,IDERD_DATA ; do a read of the drive's data register
call IDE_read16
mov r2,param1
mov r3,param2 ; high byte of cluster number in r3
ldi temp,0x0f
and r3,temp ; we must ignore high 4 bits of cluster number - reserved bits
; If cluster number == $0FFFFFFF then return with CalNxCl_ChainEnd in param1; revert to initial state
ldi temp,0xFF
cp r0,temp
brne CalcNextCluster_5 ; if byte 0 (low byte) != $FF then branch forward
cp r1,temp
brne CalcNextCluster_5 ; if byte 1 != $FF then branch forward
cp r2,temp
brne CalcNextCluster_5 ; if byte 2 != $FF then branch forward
ldi temp,0x0F
cp r3,temp
brne CalcNextCluster_5 ; if byte 3 (high byte) != $0F then branch forward
ldi param1,CalNxCl_ChainEnd ; execute here if cluster number == $0FFFFFFF
ldi temp,CalNxCl_SDS
sts CALNXCL_ST,temp ; state variable now contains SeekDrive
ret
; Write new cluster number onto stack, replacing passed-in cluster number
CalcNextCluster_5:
in ZL,SPL
in ZH,SPH ; Z points to the low byte of the original
adiw ZL,3 ; passed-in cluster number
st Z+,r0 ; write low byte of new cluster number
st Z+,r1
st Z+,r2
st Z+,r3 ; write high byte of new cluster number
; Store this new cluster number in CALNXCL_CLUS_LO
sts CALNXCL_CLUS_LO,r0 ; store low byte first
sts CALNXCL_CLUS_LO+1,r1
sts CALNXCL_CLUS_LO+2,r2
sts CALNXCL_CLUS_LO+3,r3
; read_counter = 254 - CALNXCL_OF
ldi temp2,254 ; temp2 will be read_counter
lds temp,CALNXCL_OF
sub temp2,temp ; read_counter = 254 - CALNXCL_OF
; if read_counter == 0 then zero CALNXCL_CLUS_HI and hop forward (out of indented code)
brne CNC_GC_1 ; branch forward if read_counter != 0
rjmp CalcNextCluster_8 ; otherwise jump out because read_counter == 0
CNC_GC_1:
; next_cluster_temp = CALNXCL_CLUS_LO + 1 (a prediction of what we want the next cluster to be)
ldi temp,1
add r0,temp ; next_cluster_temp is r3:r0
ldi temp,0
adc r1,temp
adc r2,temp
adc r3,temp
; Read the next 4 bytes (2 16-bit drive reads) - this is the next cluster number
; if this next cluster number != next_cluster_temp then zero CALNXCL_CLUS_HI and hop forward (out of indented code)
ldi param1,IDERD_DATA ; do a read of the drive's data register
call IDE_read16 ; param2:param1 contains low "next cluster" nibble
cp param1,r0 ; compare low bytes of next cluster versus next_cluster_temp
brne CalcNextCluster_8 ; and branch if not the same
cp param2,r1
brne CalcNextCluster_8 ; similarly for the next byte
ldi param1,IDERD_DATA ; do a read of the drive's data register
call IDE_read16 ; param2:param1 contains high "next cluster" nibble
cp param1,r2 ; compare bytes of next cluster versus next_cluster_temp
brne CalcNextCluster_8 ; and branch if not the same
cp param2,r3
brne CalcNextCluster_8 ; similarly for the highest byte
; CALNXCL_CLUS_HI = next_cluster_temp - 1
; decrement read_counter by 2
ldi temp,1
sub r0,temp
ldi temp,0
sbc r1,temp
sbc r2,temp
sbc r3,temp ; next_cluster_temp = next_cluster_temp - 1
sts CALNXCL_CLUS_HI,r0 ; and store result in CALNXCL_CLUS_HI
sts CALNXCL_CLUS_HI+1,r1
sts CALNXCL_CLUS_HI+2,r2 ; NOTE we'll have to add 2 to next_cluster_temp when incrementing
sts CALNXCL_CLUS_HI+3,r3 ; it, to make up for this -1 we just did
subi temp2,2 ; read_counter = read_counter - 2
; WHILE read_counter != 0 do {
breq CalcNextCluster_9 ; branch out of the WHILE if read_counter reaches zero
CalcNextCluster_6:
; next_cluster_temp += 1
ldi temp,2
add r0,temp ; NOTE that we're adding 2, because when we did the
ldi temp,0 ; CALNXCL_CLUS_HI = next_cluster_temp - 1 instruction,
adc r1,temp ; we decremented next_cluster_temp by 1 while doing it
adc r2,temp
adc r3,temp
; Read the next 4 bytes (2 16-bit drive reads) - this is the next cluster number
; if this next cluster number != next_cluster_temp then break out of the while loop
ldi param1,IDERD_DATA ; do a read of the drive's data register
call IDE_read16 ; param2:param1 contains low "next cluster" nibble
cp param1,r0 ; compare low bytes of next cluster versus next_cluster_temp
brne CalcNextCluster_9 ; and branch out of the WHILE if not the same
cp param2,r1
brne CalcNextCluster_9 ; similarly for the next byte
ldi param1,IDERD_DATA ; do a read of the drive's data register
call IDE_read16 ; param2:param1 contains high "next cluster" nibble
cp param1,r2 ; compare bytes of next cluster versus next_cluster_temp
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -