⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mp3drq.asm

📁 这项工程将让您把自己的MP3播放器平台
💻 ASM
📖 第 1 页 / 共 2 页
字号:
	.TITLE	(STA013 Data Request Interrupt Service)
	.sbttl	Copyright (C) 2005 by Spare Time Gizmos.  All rights reserved.


;++
; mp3drq.a51
;
; Copyright (C) 2005 by Spare Time Gizmos.  All rights reserved.
;
; This file is part of the Spare Time Gizmos' MP3 Player firmware.
;
; This firmware is free software; you can redistribute it and/or modify it under
; the terms of the GNU General Public License as published by the Free Software
; Foundation; either version 2 of the License, or (at your option) any later
; version.
;
; This program is distributed in the hope that it will be useful, but WITHOUT
; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
; more details.
;
; You should have received a copy of the GNU General Public License along with
; this program; if not, write to the Free Software Foundation, Inc., 59 Temple
; Place, Suite 330, Boston, MA  02111-1307  USA
; 
; DESCRIPTION:
;   This module contains the STA013 data request (MP3 data, that is) interrupt
; service.  The STA013 wants its MP3 data delivered bit serially with a synch-
; ronous clock.  In DEBUG mode we do this the hard way, by bit banging two
; port pins.  Needless to say, this is a little slow and limits the bit rate
; of the MP3s we can play to around 32kbps or so.
; 
;   In non-DEBUG mode we can use the 8051's built in UART in mode 0 to shift
; out the bits; this is vastly faster and is able to play 128kbps MP3s without
; missing a beat (literally!), however there is a small downside.  The 8051's
; UART sends data LSB first, which is the opposite of the way the STA013 wants
; to see it.  There's no choice but to reverse the order of the bits in each
; byte - to prevent this expensive operation from eating up all the time we
; save, we use a 256 byte lookup table to handle the reversal.
; 
;   The MP3 file data is buffered in a very simple circular buffer stored in
; xdata RAM.  In practice this buffer is always an exact multiple of 512 bytes
; so that IDE sectors can be read directly without any nasty wrap around 
; problems, but this code actually doesn't care what size the buffer is.
; 
; REFERENCES:
; 	http://www.pjrc.com/tech/mp3/sta013.html
;
; REVISION HISTORY:
; dd-mmm-yy     who     description
; 29-May-05	RLA	New File
; 12-Jul-05	DJA	ported to ASX8051/SDCC
; 13-Jul-05	RLA	Unfortunately you can't test for the play list empty
;			  simply by saying "if (g_pPlayListHead == NULL)"
;			  because MP3DRQ always holds the last BCB pointer in
;			  R0.  Invent IsPlayListEmpty() to fix this problem.
; 20-Jul-05	DJA	Port attempt II
; 20-Oct-05	RLA	Correct for SDCC's little endian byte ordering
;		RLA	Missing a # (immedate mode) when setting SCON!
;--

	.module	MP3DRQ
	.optsdcc -mmcs51 --model-small
	.radix	 d
	.include "player.inc"
	.include "fastsdi.inc"

	.globl	_InitializeMP3DRQ, _FlushPlayList, _IsPlayListEmpty
	.globl	_g_pPlayListHead, _g_pPlayListEnd, _MP3DRQ

;   These two global variables keep track of the current queue of MP3 data buffers
; to be played.  The background code pulls buffers off the free list, reads data
; blocks from the CF card, and puts full buffers on this play list.  This code pulls
; a full buffer off the list, sends it to the STA013, and then puts the buffer
; back on the free list.
	.area	DSEG (DATA)
_g_pPlayListHead:	; PUBLIC BUFFER_CONTROL_BLOCK idata *g_pPlayListHead;
	.ds	1
_g_pPlayListEnd:	; PUBLIC BUFFER_CONTROL_BLOCK idata *g_pPlayListEnd;
	.ds	1	


;   Not only do we need the register banks defined, but in this case we
; actually need _two_ banks, zero and one.  To avoid confusion, we actually
; define different (abeit non-standard) names for each bank...  See my
; insightful comments in the ide.asm module for more details...
	.area	REG_BANK_0 (REL,OVR,DATA)
	.ds	8
AR00=0x00
AR01=0x01
AR02=0x02
AR03=0x03
AR04=0x04
AR05=0x05
AR06=0x06
AR07=0x07
	.area	REG_BANK_1 (REL,OVR,DATA)
	.ds	8
AR10=0x08
AR11=0x09
AR12=0x0A
AR13=0x0B
AR14=0x0C
AR15=0x0D
AR16=0x0E
AR17=0x0F

	.area	CSEG (CODE)

;   When we're using the 8051 UART to send data to the STA013, we need to reverse
; the order of the bits in each byte first.  This 256 byte table does the job
; without wasting lots of valuable CPU time (e.g. m_abReverseBits[i] = <the reverse
; of i>).
	.if	FAST_SDI
m_abReverseBits:
;		 00   01   02   03   04   05   06   07   08   09   0a   0b   0c   0d   0e   0f
	.db	0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0 ; 00
	.db	0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8,0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8 ; 10
	.db	0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4,0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4 ; 20
	.db	0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec,0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc ; 30
	.db	0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2,0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2 ; 40
	.db	0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea,0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa ; 50
	.db	0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6,0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6 ; 60
	.db	0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee,0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe ; 70
	.db	0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1,0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1 ; 80
	.db	0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9,0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9 ; 90
	.db	0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5,0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5 ; a0
	.db	0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed,0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd ; b0
	.db	0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3,0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3 ; c0
	.db	0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb,0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb ; d0
	.db	0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7,0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7 ; e0
	.db	0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef,0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff ; f0
	.endif


;++
; PRIVATE void MP3DRQ (void) interrupt 0 using 1
;
;   As you've probably figured out by now, this routine is the interrupt service
; for STA013 MP3 data requests.  In twenty words or less, what we want to do is
; to pull a buffer of MP3 data from the m_pPlayList and send it, synchronous bit
; serially, to the STA013.  When we've finished sending the buffer we return it
; to the free buffer pool, m_pFreeBufferList, and then go back to m_pPlayList to
; look for another buffer to play.
;
;   There are only two things that make our life difficult - the first is that
; the STA013's internal buffer can only hold 200 bytes or so (the datasheet is
; a little vague on that topic!) where as our buffers hold 512.  That means it'll
; take several MP3 DRQ interrupts to transfer a single buffer.
;
;   Second, if the background code is too slow it's possible that we'll run out
; of buffers on the m_pPlayList.  We can't simply return if that happens because
; the STA013 will continue to assert DRQ which will continue to hold the 8051's
; INT0 low.  Since INT0 is configured as level sensitive, we'll never get out of
; this interrupt routine!  The only solution is to mask INT0 interrupts with the
; EX0 bit when this happens.  The background code will turn EX0 back on when it
; reads the next buffer.
;
;   This routine has its own register bank allocated to it, so when we enter
; routine we can depend on R0..R7 having the same values we left there the last
; time we were here.  In general, the register usage is:
;
;	R0 - points to current buffer control block (BCB)
;	R1 - temporary (copy of R0, and loop counter)
;	R2
;	R3
;	R4 - count of bytes remaining in current buffer (high)
;	R5 -  "  "  "  "     "    "   "   "   "  "   "  (low)
;	R6 - address (xdata) of next byte in current buffer (DPH)
;	R7 -  "   "     "    "    "   "   "    "  "   "  "  (DPL)
;--

;   Unlike A51, with sdcc/asx8051 there's no need (and indeed, no way!) to set
; the interrupt vector here to point to MP3DRQ.  The SDCC compiler will take
; care of it for us in the "main" module (player.c in this case) SO LONG AS
; we include a function prototype for the MP3DRQ routine.  The compiler looks
; for the "interrupt" attribute on the prototype declaration and automatically
; generates the appropriate vector.

;   A further unpleasant business is that asx8051 has nothing equivalent to
; the "USING" directive.  The only way to handle different register banks is
; to actually equate the AR0..7 symbols to different addresses.  Since this
; module actually uses both bank zero (for code that's called directly from
; C) and bank one (for this ISR) we actually define a different set of symbols
; (e.g. AR01 for register 1, bank 0 - AR10 for register 0, bank 1) to avoid
; confusion...  Of course, it's still up to us, the programmers, to ensure
; that the correct bank is selected by the PSW at the correct time!

_MP3DRQ:
	PUSH 	ACC			; save the accumulator
	PUSH 	DPH			;  ... data pointer
	PUSH 	DPL			;  ...
	PUSH 	PSW			; register bank and flags
	SETB 	LED_BIT			; (turn the LED on for timing purposes)
	MOV  	PSW, #0x08		; and select register bank #1

;   If there's no current buffer, try to remove the next one from the
; play list.  If the play list is empty, then disable the DRQ interrupts
; and wait for more to arrive.  As long as we leave with R0=0, we'll try
; to obtain a play buffer next time we come here.
ISR10:	CJNE	R0, #0, ISR20		; jump if the current BCB is not null
	MOV  	R0, _g_pPlayListHead	; remove the next BCB from the play list
	CJNE	R0, #0, ISR11		; jump if we really got a buffer
	CLR  	EX0			; no more buffers - disable future interrupts
	SJMP 	ISR99			; and go dismiss this one

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -