📄 fat32.s
字号:
ldi param1,IDERD_DATA ; read bytes 36 & 37
call IDE_read16 ; low byte in param1; high in param2
mov r0,param1
mov r1,param2 ; r0 is low byte, r1 is next
ldi param1,IDERD_DATA ; read bytes 38 & 39
call IDE_read16 ; low byte in param1; high in param2
mov r2,param1
mov r3,param2 ; r2 is next byte, r3 is high byte
; shift left 1 bit (to multiply BPD_FATSz32 by 2)
lsl r0
rol r1
rol r2
rol r3
; add 63 + BPD_ResvdSecCnt (think of it as: 0 0 0 (63+BPD_ResvdSecCnt) plus r3 r2 r1 r0 equals r3 r2 r1 r0)
ldi temp,63
add temp,r4 ; temp contains 63 + BPD_ResvdSecCnt (low byte only; BPD_ResvdSecCnt assumed small)
add r0,temp ; note this sets carry flag as required
ldi temp,0 ; ldi does not affect carry flag
adc r1,temp
adc r2,temp
adc r3,temp ; just moving the carry flag through
; Store result in DIR_START_SEC and return
ldi ZL,lo8(DIR_START_SEC)
ldi ZH,hi8(DIR_START_SEC) ; point Z to DIR_START_SEC
st Z+,r0
st Z+,r1
st Z+,r2
st Z,r3 ; store result, low byte first
pop r4
pop r3
pop r2
pop r1
pop r0
pop ZH
pop ZL
pop param2
pop param1
pop temp
ret
;**********************************************************************************
;*
;* StreamFileC
;*
;* Same as StreamFile below, but preserves all the registers.
;*
;**********************************************************************************
StreamFileC:
push temp
push temp2
push param1
push param2
push XL
push XH
push YL
push YH
push ZL
push ZH
push r0
push r1
push r2
push r3
push r20
push r21
; ldi param1,'S'
; call UART_TxCharWait
; ldi param1,'F'
; call UART_TxCharWait
; ldi param1,'C'
; call UART_TxCharWait
; ldi param1,'-'
; call UART_TxCharWait
; ldi param1,'i'
; call UART_TxCharWait
; ldi param1,'n'
; call UART_TxCharWait
; ldi param1,'\r'
; call UART_TxCharWait
; ldi param1,'\n'
; call UART_TxCharWait ; short debug message
rcall StreamFile
; ldi param1,'S'
; call UART_TxCharWait
; ldi param1,'F'
; call UART_TxCharWait
; ldi param1,'C'
; call UART_TxCharWait
; ldi param1,'-'
; call UART_TxCharWait
; ldi param1,'o'
; call UART_TxCharWait
; ldi param1,'u'
; call UART_TxCharWait
; ldi param1,'t'
; call UART_TxCharWait
; ldi param1,'\r'
; call UART_TxCharWait
; ldi param1,'\n' ; short debug message
; call UART_TxCharWait
pop r21
pop r20
pop r3
pop r2
pop r1
pop r0
pop ZH
pop ZL
pop YH
pop YL
pop XH
pop XL
pop param2
pop param1
pop temp2
pop temp
ret
;**********************************************************************************
;*
;* StreamFile
;*
;* Main loop routine - this routine is called in the main loop.
;* This means no parameters passed in as-such (via param1,2, stack, etc) - everything
;* is communicated to this routine via variables.
;*
;* This routine "streams" a file off the disk and into the specified queue.
;* It is told (in variables) the starting cluster number of the file, as well as
;* the file length. It then streams that file into the queue until either it's
;* commanded to stop, or the file comes to an end. Simple enough concept.
;*
;* Because this routine sometimes has to wait for the drive to seek to the next cluster,
;* it's implemented as a state machine. While it's waiting for the drive to seek it
;* will just return.
;*
;* To prevent this routine from hogging too much CPU time, it limits the number of
;* reads it performs from the drive, ie, it only streams a certain number of bytes
;* into the queue at a time. This is important because with a large MP3 data queue
;* it can spend so much time filling it that the task giving the data to the MP3 codec
;* chip may not get sufficient time, resulting in breaks in music playback. The number
;* of bytes that may be read per execution MUST be an 8-bit EVEN number, and is
;* kept as the constant (.equ) StreamFile_MaxBytes.
;*
;* The file length may be an odd number. It is the only odd "number of bytes"
;* number permitted. All other "byte count" type numbers MUST be even. This is because
;* bytes are read from the drive in pairs - the drive has a 16-bit bus. When we
;* determine the number of free bytes in the MP3 data queue we zero the LSB of the
;* result to ensure it too is an even number.
;*
;* From experience with my test drive, I've found that although I've commanded it to
;* read an entire cluster, it sometimes returns "busy" after reading only a couple
;* of sectors worth of data from that cluster. Hence... this routine is forced to
;* keep track of sectors, and check that the drive is ready before reading from
;* a new sector boundary. It keeps track of the number of bytes remaining in
;* the current sector in STREAMFILE_SEC_REM (it contains 512 & is decremented to zero).
;* The number of sectors remaining in the current cluster is stored in
;* STREAMFILE_SECCLUS_REM (which starts at SEC_PER_CLUS and decrements to zero).
;* When a sector is finished (STREAMFILE_SEC_REM==0) the drive is checked to make
;* sure it's not busy. That's the purpose of the WaitDriveNotBusy state.
;* When a cluster is finished (STREAMFILE_SECCLUS_REM==0)
;* the drive is seeked to the next cluster. All provided that we haven't been
;* commanded to stop; the file is not finished; etc.
;*
;* This routine may be commanded to stop at any time. For example, a file may be
;* playing and the user may press the "stop" key. Also, the file may end for a
;* couple of reasons. One is that the end of the file is reached (length has decremented
;* all the way down to zero). Another is that the file cluster chaining may have a
;* problem, causing the CalcNextCluster routine to return a ChainEnd error message.
;*
;* This routine knows only 3 commands: Stop, Stream, and StreamWithClusterNumbers.
;* StreamWithClusterNumbers inserts the 4-byte cluster number into the data Q low-byte first
;* at the beginning of that cluster's data.
;*
;* Any routine wanting to know what this routine is doing may examine the state variable
;* STREAMFILE_ST. If a routine wants this routine to stream a file, it MUST wait until this
;* routine is idle (it can force idle by issuing a stop command) before loading up the
;* parameters and issuing the stream command. Changing the parameters while this routine
;* is streaming a file will have unpredictable results.
;*
;* This routine functions as follows:
;*
;*
;* IDLE state: (STREAMFILE_ST == StreamFile_St_Idle)
;* If Command == Stop then return, else (command == stream):
;* If filesize == 0 then
;* Write Stop into command variable & return
;* Change state variable to SeekCluster state
;* Jump to SeekCluster state
;*
;* SeekCluster State (STREAMFILE_ST == StreamFile_St_SeekClus)
;* If SeekSector is idle then
;* change state to SeekCluster2
;* if Command == StreamFile_Cmd_SmWithCl put cluster number (STREAMFILE_CLUS) in Q, low byte first
;* return
;* else (SeekSector busy)
;* force SeekSector idle & return
;*
;* SeekCluster2 State (STREAMFILE_ST == StreamFile_St_SeekClus2)
;* Store 512 in STREAMFILE_SEC_REM (# of bytes remaining in this sector)
;* Store SEC_PER_CLUS in STREAMFILE_SECCLUS_REM (# of sectors remaining in this cluster)
;* Read cluster number from STREAMFILE_CLUS
;* Convert to a sector number & push onto stack
;* Get number of sectors per cluster (SEC_PER_CLUS) into param1
;* Command a seek to this sector (call SeekSector)
;* If SeekSector does not return "done" then Return, (we'll return to this state to try again)
;* else (SeekSector is finished)
;* Change state variable to ReadData state
;* Jump to ReadData state
;*
;* ReadData State (STREAMFILE_ST == StreamFile_St_ReadData)
;* If Command == Stop then change state variable to idle state and return.
;* Determine the lowest of:
;* Bytes remaining in the file (32 bits) (odd or even)
;* Free bytes remaining in the MP3 data queue (16 bits) (even only)
;* Bytes remaining in the current sector (16 bits) (even only)
;* Number of bytes read permitted per execution (8 bits) (even only)
;* Call this result NumBytes (8 bit number) - keep a copy (NumberBytesRead)
;* If NumBytes == 0 then return
;* Read 2 bytes from drive
;* Place first byte in MP3 data queue
;* Decrement NumBytes
;* If NumBytes == 0 then exit this loop
;* Place second byte in MP3 data queue
;* Decrement NumBytes
;* If NumBytes != 0 then loop back to read from drive again
;* Filesize (STREAMFILE_FILESZ) = Filesize - NumberBytesRead
;* If FileSize == 0 then
;* Write "stop" into command variable
;* Change state variable to Idle state & return
;* BytesRemainingInSector (STREAMFILE_SEC_REM) = BytesRemainingInSector - NumberBytesRead
;* If BytesRemainingInSector != 0 then
;* Return
;* Else
;* Decrement SectorsRemainingInCluster (STREAMFILE_SECCLUS_REM)
;* If SectorsRemainingInCluster == 0 then
;* Change state variable to LearnNextCluster
;* Jump to LearnNextCluster state
;* Change state variable to WaitDriveNotBusy
;* Jump to WaitDriveNotBusy state
;*
;* WaitDriveNotBusy state (STREAMFILE_ST == StreamFile_St_WaitDrv)
;* Read drive status register
;* If drive busy (BSY flag set) then return
;* If drive not ready (RDY flag clear) then return
;* Change state variable to ReadData
;* Store 512 in STREAMFILE_SEC_REM (# of bytes remaining in this sector)
;* Return
;*
;* LearnNextCluster state (STREAMFILE_ST == StreamFile_St_LearnClus)
;* Get current cluster number (STREAMFILE_CLUS)
;* Push it onto stack
;* Call CalcNextCluster
;* If result code == Busy then return
;* If result code == ChainEnd then
;* Write "stop" into command variable
;* Change state variable to Idle state & return
;* else (result code == Done)
;* Get new cluster number off stack & write into STREAMFILE_CLUS
;* Change state variable to SeekCluster
;* Jump to SeekCluster state
;*
;*
;* Accepts: Commands in STREAMFILE_CMD (8 bits)
;* Initial file cluster number in STREAMFILE_CLUS (32 bits)
;* Initial file length in STREAMFILE_FILESZ (32 bit number of bytes)
;* Queue to stream into in STREAMFILE_QPOINTER (16 bit pointer)
;* Returns: State of the routine in STREAMFILE_ST (8 bits)
;* Uses: temp, temp2, param1, param2, X, Y, Z, r0, r1, r2, r3, r20, r21, flags
;*
;**********************************************************************************
StreamFile:
; If Command == Stop then change to idle state & return. Dangerous, because if StreamFile
; has kicked off another state machine, then suddenly stops (here), that other state machine
; will remain in an undefined state. Probably better to stop only at controlled times.
; lds temp,STREAMFILE_CMD ; get command
; cpi temp,StreamFile_Cmd_Stop
; brne StreamFileSD1 ; branch forward if command NOT stop
; ldi temp,StreamFile_St_Idle ; but if command is stop...
; sts STREAMFILE_ST,temp ; change to IDLE state
; ret ; and return
StreamFileSD1:
; Jump to the correct state (this is the state dispatcher code)
ldi ZL,lo8(STREAMFILE_ST)
ldi ZH,hi8(STREAMFILE_ST) ; point Z to StreamFile state variable
ld temp,Z ; get the state
cpi temp,StreamFile_St_Idle
breq StreamFile_IDL_J ; branch if state is Idle
cpi temp,StreamFile_St_SeekClus
breq StreamFile_SC_J ; branch if state is SeekCluster
cpi temp,StreamFile_St_SeekClus2
breq StreamFile_SC2_J ; branch if state is SeekCluster2
cpi temp,StreamFile_St_ReadData
breq StreamFile_RD_J ; branch if state is ReadData
cpi temp,StreamFile_St_WaitDrv
breq StreamFile_WD_J ; branch if state is WaitDriveNotBusy
cpi temp,StreamFile_St_LearnClus
breq StreamFile_LC_J ; branch if state is LearnNextCluster
ldi temp,StreamFile_St_Idle ; otherwise it's an unknown state
st Z,temp ; so make the state to be idle
ret ; and return
; jump table (because branches can't reach far enough in this routine)
StreamFile_IDL_J: jmp StreamFile_IDL
StreamFile_SC_J: jmp StreamFile_SC
StreamFile_SC2_J: jmp StreamFile_SC2
StreamFile_RD_J: jmp StreamFile_RD
StreamFile_WD_J: jmp StreamFile_WD
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -