📄 mp3drq.asm
字号:
; R0 contains the address of the next buffer on the play list. Update
; m_pPlayListHead to remove that buffer from the list...
ISR11: MOV _g_pPlayListHead, @R0 ; ... m_pPlayListHead = m_pPlayListHead->pNext
MOV R1, AR10 ; copy R0 so we don't have to restore it later
; Load R6/R7 with the address (in xdata) of the actual data buffer that goes
; with this BCB, and load R4/R5 with the count of bytes (up to 512) in this buffer.
INC R1 ; pbBuffer is the second field in the BCB
MOV A, @R1 ; ... get the low byte
MOV R7, A ; ... we keep that in R7
INC R1 ; and then the high byte
MOV A, @R1 ; ...
MOV R6, A ; ... (R6/R7) = (R0)->pbBuffer;
INC R1 ; cbBuffer is the next field in the BCB
MOV A, @R1 ; ... low byte
MOV R5, A ; ...
INC R1 ; ...
MOV A, @R1 ; ... and high byte
MOV R4, A ; ... (R4/R5) = (R0)->cbBuffer;
; Here if the current buffer and is valid and, by implication, so are R4/R5
; and R6/R7....
ISR20: MOV DPL, R7 ; load DPTR with the address of the next byte
MOV DPH, R6 ; ...
MOVX A, @DPTR ; load the next byte of MP3 data
INC DPTR ; and increment the buffer pointer
MOV R7, DPL ; save the new address for next time
MOV R6, DPH ; ...
.if FAST_SDI
; In FAST_SDI mode we need to reverse the order of the bits in this byte
; (using m_abReverseBits, of course) and then send it to SBUF. The 8051
; UART hardware will handle the rest. Note that it's not necessary to
; check the TI flag here because the 8051 will shift out the byte in only
; 9 machine cycles, and this loop takes far longer than that!
MOV DPTR, #m_abReverseBits ; point to m_abReverseBits
MOVC A, @A+DPTR ; lookup m_abReverseBits[data]
MOV SBUF, A ; and send that to the STA013
.else
; In DEBUG (non-FAST_SDI) mode, we have to shift out the eight bits
; one at a time, MSB first, and toggle SCK (serial clock) for each one.
MOV R1, #8 ; shift out 8 bits
ISR21: RLC A ; move the MSB into the carry
MOV STA013_ALT_SDI, C ; output the data bit
SETB STA013_ALT_SCK ; pulse the clock
CLR STA013_ALT_SCK ; ...
DJNZ R1, ISR21 ; and loop for 8 bits
.endif
; Decrement the count of bytes remaining (kept in R4/R5) and, if it's zero,
; put this buffer on the free list and clear R0. Clearing R0 will make the
; code at the beginning of this ISR attempt to pull a new buffer off the play
; list the next time around.
DEC R5 ; decrement the LSB of cbBuffer
CJNE R5, #0xFF, ISR30 ; jump if there's no borrow
DEC R4 ; ... and decrement the MSB if there is
ISR30: MOV A, R5 ; see if cbBuffer == 0
ORL A, R4 ; ...
JZ ISR31 ; jump if this buffer is empty
; The current buffer still has data. If the STA013 is still asserting DRQ,
; then loop back and send it another byte. If, however, the STA013 has rel-
; eased DRQ then we can safely exit from this interrupt. Remember that the
; DRQ input is active LOW!!!
JNB STA013_DATAREQ, ISR20 ; jump if the STA013 wants more data
SJMP ISR99 ; nope - go dismiss this interrupt
; Here if we've sent all the bytes in this buffer. First, put the current
; buffer on the free list (otherwise it'll be lost forever!) and the get a
; new buffer...
ISR31: MOV @R0, _g_pFreeBufferList ; (R0)->pNext = m_pFreeBufferList
MOV _g_pFreeBufferList, R0 ; m_pFreeBufferList = R0
MOV R0, #0 ; R0 == NULL indicates no current buffer
; If the STA013 is still asserting DRQ (remember that its active low!) then
; try to get another buffer now. Otherwise, we can safely exit now as long
; as R0 contains NULL...
JNB STA013_DATAREQ, ISR10 ; jump if the STA013 wants more data
; SJMP ISR99 ; nope - go dismiss this interrupt
; One way or another, we're all done with this interrupt. Restore the
; original registers and register bank and return...
ISR99: CLR LED_BIT ; (LED off for timing purposes)
POP PSW ; restore register bank and flags
POP DPL ; restore other registers
POP DPH ; ...
POP ACC ; ...
RETI ; and dismiss the interrupt
;++
; BOOL IsPlayListEmpty (void)
;
; This routine will return TRUE if the current MP3 buffer queue is empty
; and FALSE if data still remains. You might be tempted to simply say
; "if (g_pPlayListHead == NULL)", but that doesn't work because MP3DRQ actually
; removes the current buffer from the play list. Therefore g_pPlayListHead
; will become NULL even though there's still one more buffer left in the
; works...
;--
_IsPlayListEmpty:
; The first thing is just to test the play list queue - if g_pPlayListHead
; isn't null, then it's guaranteed that the play list isn't empty...
MOV DPL, #0 ; assume the result is FALSE
MOV A, _g_pPlayListHead ; check the play list queue
JZ EMPTY1 ; jump if it's empty
RET ; not empty - return FALSE now
; Here if the queue is empty - check to see if there's still a buffer "in
; progress" (the pointer to the BCB will in R0 of register bank #1). Note
; that this code cheats a bit, bit it's must faster than explicitly changing
; register banks (which requires disabling interrupts and the whole thing!).
EMPTY1: MOV A, AR10 ; fetch R0 from register bank 1
JZ EMPTY2 ; jump if pBCB == NULL
RET ; not null - return FALSE
; Here if the play list really is empty...
EMPTY2: MOV DPL, #0xFF ; return TRUE
RET ; and we're outta here
;++
; void FlushPlayList (void)
;
; This routine will flush the MP3 "to be played" buffer queue, plus any
; buffer that's currently playing now. This doesn't actually stop the
; STA013 decoder, and it's probably a good idea for the caller to do that
; first!
;--
_FlushPlayList:
PUSH IE ; all interrupts off while we change
CLR EA ; ... the register bank
MOV PSW, #0x08 ; select register bank 1
; Put the currently playing buffer (pBCB is in R0) on the free list...
CJNE R0, #0, FLUSH1 ; is there a current buffer ?
SJMP FLUSH2 ; nope - skip this
FLUSH1: MOV @R0, _g_pFreeBufferList ; (R0)->pNext = m_pFreeBufferList
MOV _g_pFreeBufferList, R0 ; m_pFreeBufferList = R0
; Now return all the buffers in the playlist to the free pool...
FLUSH2: MOV R0, _g_pPlayListHead ; remove the next BCB from the play list
CJNE R0, #0, FLUSH3 ; jump if we really got a buffer
SJMP FLUSH4 ; no more buffers on the list
FLUSH3: MOV _g_pPlayListHead, @R0 ; ... m_pPlayListHead = m_pPlayListHead->pNext
MOV @R0, _g_pFreeBufferList ; (R0)->pNext = m_pFreeBufferList
MOV _g_pFreeBufferList, R0 ; m_pFreeBufferList = R0
SJMP FLUSH2 ; and keep flushing
; Reset all the registers and then we can return...
FLUSH4: SJMP INIT10 ; (re)initialize all registers and return
;++
; void InitializeMP3DRQ (void)
;
; This routine will initialize the MP3 data request handler. It will
; configure the 8051's external interrupt hardware as required and it will
; initialize the register bank used by the ISR. It does NOT, however, turn
; on the interrupts!
;--
_InitializeMP3DRQ:
; Initialize the register bank used by the ISR...
PUSH IE ; no interrupts while we've changed
CLR EA ; ... the register bank
MOV PSW, #0x08 ; select register bank 1
INIT10: MOV A, #0 ; clear registers 0..7
MOV R0, A ; ...
MOV R1, A ; ...
MOV R2, A ; ...
MOV R3, A ; ...
MOV R4, A ; ...
MOV R5, A ; ...
MOV R6, A ; ...
MOV R7, A ; ...
; Initialize the playback buffer queue to empty. Note that if there
; are any _real_ buffers on this list (if, for example, we just aborted
; a playback) then the caller had better free them before calling this
; routine again!
MOV _g_pPlayListHead, #0
MOV _g_pPlayListEnd, #0
; Now we can re-enable interrupts...
MOV PSW, #0x00 ; back to the default register bank
POP IE ; and restore interrupts
.if FAST_SDI
; For FAST_SDI mode, configure the 8051's hardware UART to mode 0
; (synchronous shift register). This mode is automatically clocked
; at 1/6th the CPU clock (on a 6 clock per cycle Philips part; it's
; 1/12th the CPU clock on a regular 8051), so no timer is needed to
; generate a baud rate.
MOV SCON, #0x1C ; mode 0
.else
; For DEBUG (slow) mode, configure the alternate STA013 clock and
; data outputs....
CLR STA013_ALT_SCK ; clock low
CLR STA013_ALT_SDI ; and data low too
.endif
; Configure the 8051's interrupt system...
CLR IT0 ; INT0 level sensitive
; Set interrupt priority here???
RET
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -