Here's our example binary for folks to try. It works on real hardware and on Fusion 3.64 (remember that 3.63 sounds like crap). If you use Gens/GS release 7 with my DMA PWM modifications, you need to make one more change to pwm.c to use int-driven dma pwm:
Change this
Code: Select all
if (PWM_Mode & 0x0080)
{
// RPT => generate DREQ1 as well as INT
SH2_DMA1_Request(&M_SH2, 1);
SH2_DMA1_Request(&S_SH2, 1);
}
Code: Select all
if (PWM_Mode & 0x0080)
{
// RPT => generate DREQ1 as well as INT
SH2_DMA1_Request(&M_SH2, 1);
SH2_DMA1_Request(&S_SH2, 1);
if ((SH2_Read_Long(&S_SH2, 0xFFFFFF9C) & 7) == 7)
SH2_Interrupt_Internal(&S_SH2, (SH2_Read_Long(&S_SH2, 0xFFFFFFA8)<<8) | ((SH2_Read_Word(&S_SH2, 0xFFFFFEE2) >> 8) & 0x000F));
}
So how do we do interrupt driven dma pwm audio? First, assign the dma an exception entry in the exception table... I suggest the exception right after the autovectors since it's the first free entry not used by anything else, and easy to find. So your table looks like this at the end:
Code: Select all
.long slave_pwm /* PWM interupt (Level 6 & 7) */
.long slave_cmd /* Command interupt (Level 8 & 9) */
.long slave_hbi /* H Blank interupt (Level 10 & 11 */
.long slave_vbi /* V Blank interupt (Level 12 & 13) */
.long slave_rst /* Reset Button (Level 14 & 15) */
.long slave_dma1 /* DMA1 TE INT */
Code: Select all
!-----------------------------------------------------------------------
! Slave DMA 1 TE INT handler
!-----------------------------------------------------------------------
slave_dma1:
! save registers
sts.l pr,@-r15
mov.l r0,@-r15
mov.l r1,@-r15
mov.l r2,@-r15
mov.l r3,@-r15
mov.l r4,@-r15
mov.l r5,@-r15
mov.l r6,@-r15
mov.l r7,@-r15
mov.l sd1_handler,r0
jsr @r0
nop
! restore registers
mov.l @r15+,r7
mov.l @r15+,r6
mov.l @r15+,r5
mov.l @r15+,r4
mov.l @r15+,r3
mov.l @r15+,r2
mov.l @r15+,r1
mov.l @r15+,r0
lds.l @r15+,pr
rte
nop
.align 2
sd1_handler:
.long _slave_dma1_handler
Code: Select all
void slave(void)
{
uint16_t sample, ix;
// init DMA
SH2_DMA_SAR0 = 0;
SH2_DMA_DAR0 = 0;
SH2_DMA_TCR0 = 0;
SH2_DMA_CHCR0 = 0;
SH2_DMA_DRCR0 = 0;
SH2_DMA_SAR1 = 0;
SH2_DMA_DAR1 = 0x20004034; // storing a long here will set left and right
SH2_DMA_TCR1 = 0;
SH2_DMA_CHCR1 = 0;
SH2_DMA_DRCR1 = 0;
SH2_DMA_DMAOR = 1; // enable DMA
SH2_DMA_VCR1 = 72; // set exception vector for DMA channel 1
SH2_INT_IPRA = (SH2_INT_IPRA & 0xF0FF) | 0x0F00; // set DMA INT to priority 15
// init the sound hardware
MARS_PWM_MONO = 1;
MARS_PWM_MONO = 1;
MARS_PWM_MONO = 1;
if (MARS_VDP_DISPMODE & MARS_NTSC_FORMAT)
MARS_PWM_CYCLE = (((23011361 << 1)/SAMPLE_RATE + 1) >> 1) + 1; // for NTSC clock
else
MARS_PWM_CYCLE = (((22801467 << 1)/SAMPLE_RATE + 1) >> 1) + 1; // for PAL clock
MARS_PWM_CTRL = 0x0185; // TM = 1, RTP, RMD = right, LMD = left
sample = SAMPLE_MIN;
/* ramp up to SAMPLE_CENTER to avoid click in audio (real 32X) */
while (sample < SAMPLE_CENTER)
{
for (ix=0; ix<(SAMPLE_RATE*2)/(SAMPLE_CENTER - SAMPLE_MIN); ix++)
{
while (MARS_PWM_MONO & 0x8000) ; // wait while full
MARS_PWM_MONO = sample;
}
sample++;
}
// initialize mixer
MARS_SYS_COMM6 = MIXER_UNLOCKED; // sound subsystem running
fill_buffer(&snd_buffer[0]); // fill first buffer
slave_dma1_handler(); // start DMA
SetSH2SR(2);
while (1)
{
if (MARS_SYS_COMM4 == SSH2_WAITING)
continue; // wait for command
// do command in COMM4
// done
MARS_SYS_COMM4 = SSH2_WAITING;
}
}
So how about that exception function?
Code: Select all
void slave_dma1_handler(void)
{
static int32_t which = 0;
while (MARS_SYS_COMM6 == MIXER_LOCK_MSH2) ; // locked by MSH2
SH2_DMA_CHCR1; // read TE
SH2_DMA_CHCR1 = 0; // clear TE
if (which)
{
// start DMA on first buffer and fill second
SH2_DMA_SAR1 = ((uint32_t)&snd_buffer[0]) | 0x20000000;
SH2_DMA_TCR1 = num_samples; // number longs
SH2_DMA_CHCR1 = 0x18E5; // dest fixed, src incr, size long, ext req, dack mem to dev, dack hi, dack edge, dreq rising edge, cycle-steal, dual addr, intr enabled, clear TE, dma enabled
fill_buffer(&snd_buffer[MAX_NUM_SAMPLES * 2]);
}
else
{
// start DMA on second buffer and fill first
SH2_DMA_SAR1 = ((uint32_t)&snd_buffer[MAX_NUM_SAMPLES * 2]) | 0x20000000;
SH2_DMA_TCR1 = num_samples; // number longs
SH2_DMA_CHCR1 = 0x18E5; // dest fixed, src incr, size long, ext req, dack mem to dev, dack hi, dack edge, dreq rising edge, cycle-steal, dual addr, intr enabled, clear TE, dma enabled
fill_buffer(&snd_buffer[0]);
}
which ^= 1; // flip audio buffer
}
See how easy that is?
I'll be releasing the full code for the example, along with my XM player, later in the week... maybe the week end.