DMA to PWM

Ask anything your want about the 32X Mushroom programming.

Moderator: BigEvilCorporation

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Thu Jan 14, 2010 3:14 am

Snake wrote:
Graz wrote:I started with the leaked official docs, and it seemed pretty much obvious to me. I thought it was the recommended way to do things.
Well, I can tell you that the docs available at the time didn't make it at all clear :(
Yeah - looking at the diagnostic cart source isn't a very big help. All it does is play a sample once, and the code is overly compilcated by all the extra stuff it does like checking a timeout. Granted, they did that because it was a DIAGNOSTIC cart meant to work even if the hardware was bad, but SEGA needed to provide one good example of a simple double-buffer... kind of like mine. :wink:

That's one reason I posted my example here along with what I found. Devs can now do DMA audio for 32X homebrew without having to tear their hair out to figure out how. I did that for them. :lol:

I noticed Gens/GS doesn't work properly with it. I'll have to check the gens code and send another patch to the author.

Snake
Very interested
Posts: 206
Joined: Sat Sep 13, 2008 1:01 am

Post by Snake » Thu Jan 14, 2010 5:47 am

Chilly Willy wrote:Yeah - looking at the diagnostic cart
....and I don't even know where that came from - it certainly was not supplied with any of my many dev kits. I'm not even sure it was supposed to leave Sega.

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Thu Jan 14, 2010 8:12 am

Well, if SEGA didn't supply the diagnostic cart code to developers, it's no wonder nobody uses DMA sound - it's the only example of it I'm aware of. All the SEGA demos use the PWM IRQ, so it's not a wonder that that's what developers used.

One thing I was wondering - is it really necessary to bang on the FRT like in all the SEGA examples? I didn't bother in Wolf32X and no one has complained of any problems. As far as I could tell from the bulletins, the FRT nonsense seems to be for the developer cards, not the release hardware. It would be nice if homebrew could actually use the FRT.

Snake
Very interested
Posts: 206
Joined: Sat Sep 13, 2008 1:01 am

Post by Snake » Fri Jan 15, 2010 1:14 am

Chilly Willy wrote:One thing I was wondering - is it really necessary to bang on the FRT like in all the SEGA examples?
No. That's probably down to the workaround they provided for a hardware bug. Basically when you hit the reset button not everything gets reset, and this can cause the whole system to lock up. The workaround was supposed to fix this by using a timer interrupt (they should still be running) which resets the SH2s.

Only problem being that the workaround doesn't help in the slightest, because the 68K side has its reset vector mangled by the BIOS, and has just jumped to non-existant memory.

So I wouldn't worry about it ;)

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Fri Jan 15, 2010 1:31 am

Cool! Thanks! I was hoping that was the case. I've been incrementing a long in the communication regs in the vblank as the timing. Not terribly great, huh? The FRT could help, or maybe the interval timer. If the FRT really couldn't be used, I was going to try an interval timer test to see how that works. That still might be better in some cases than the FRT.

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Post by Stef » Fri Jan 15, 2010 9:23 am

Chilly Willy wrote: I noticed Gens/GS doesn't work properly with it. I'll have to check the gens code and send another patch to the author.
As far for Gens, it doesn't support DMA PWM for the simple reason that no game use it, as i can't even test my code i didn't bothered :p

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Fri Jan 15, 2010 6:18 pm

Stef wrote:
Chilly Willy wrote: I noticed Gens/GS doesn't work properly with it. I'll have to check the gens code and send another patch to the author.
As far for Gens, it doesn't support DMA PWM for the simple reason that no game use it, as i can't even test my code i didn't bothered :p
I've posted a few patches to the guy who works on Gens/GS since he's active on that. If I figure out what's wrong, he'll work the fix into the next version. I'll do that over at SonicRetro since that's his main hangout.

mic_
Very interested
Posts: 265
Joined: Tue Aug 12, 2008 12:26 pm
Location: Sweden
Contact:

Post by mic_ » Sat Jan 16, 2010 12:09 pm

Is there a version of Kega where this works ok? It's all distorted in 3.51, which makes it impossible to dev because you never know if there distortions come from a bug in your program or from the emulator.

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Sat Jan 16, 2010 5:15 pm

mic_ wrote:Is there a version of Kega where this works ok? It's all distorted in 3.51, which makes it impossible to dev because you never know if there distortions come from a bug in your program or from the emulator.
3.62 works, but it's "rough" - Snake said he'd look into it. It's not so rough that it makes it impossible to test. However, the best test for this is real hardware. I just ran my test off an SD card on my Neo Myth... quick and simple. 8)

Snake
Very interested
Posts: 206
Joined: Sat Sep 13, 2008 1:01 am

Post by Snake » Sat Jan 16, 2010 11:42 pm

mic_ wrote:Is there a version of Kega where this works ok? It's all distorted in 3.51, which makes it impossible to dev because you never know if there distortions come from a bug in your program or from the emulator.
It works perfectly in Mono, only Stereo is distorted. I'll fix it up when I get a chance.

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Sun Jan 17, 2010 2:19 am

Okay, if you have the source to gens/gs r7, here's the fix for DMA audio for the 32X. Replace src/gens/gens_core/sound/pwm.c with this:

Code: Select all

/***************************************************************************
 * Gens: PWM audio emulator.                                               *
 *                                                                         *
 * Copyright (c) 1999-2002 by Stéphane Dallongeville                       *
 * Copyright (c) 2003-2004 by Stéphane Akhoun                              *
 * Copyright (c) 2008-2009 by David Korth                                  *
 *                                                                         *
 * This program 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., *
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.           *
 ***************************************************************************/

#include "pwm.h"

#include <string.h>

#include "gens_core/mem/mem_sh2.h"
#include "gens_core/cpu/sh2/sh2.h"

#if PWM_BUF_SIZE == 8
unsigned char PWM_FULL_TAB[PWM_BUF_SIZE * PWM_BUF_SIZE] =
{
	0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
	0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40,
};
#elif PWM_BUF_SIZE == 4
unsigned char PWM_FULL_TAB[PWM_BUF_SIZE * PWM_BUF_SIZE] =
{
	0x40, 0x00, 0x00, 0x80,
	0x80, 0x40, 0x00, 0x00,
	0x00, 0x80, 0x40, 0x00,
	0x00, 0x00, 0x80, 0x40,
};
#else
#error PWM_BUF_SIZE must equal 4 or 8.
#endif /* PWM_BUF_SIZE */

unsigned short PWM_FIFO_R[8];
unsigned short PWM_FIFO_L[8];
unsigned int PWM_RP_R;
unsigned int PWM_WP_R;
unsigned int PWM_RP_L;
unsigned int PWM_WP_L;
unsigned int PWM_Cycles;
unsigned int PWM_Cycle;
unsigned int PWM_Cycle_Cnt;
unsigned int PWM_Int;
unsigned int PWM_Int_Cnt;
unsigned int PWM_Mode;
unsigned int PWM_Enable;
unsigned int PWM_Out_R;
unsigned int PWM_Out_L;

unsigned int PWM_Cycle_Tmp;
unsigned int PWM_Cycles_Tmp;
unsigned int PWM_Int_Tmp;
unsigned int PWM_FIFO_L_Tmp;
unsigned int PWM_FIFO_R_Tmp;

/* PWM scaling variables. */
static int PWM_Offset;
static int PWM_Scale;
static int PWM_Loudness;


/**
 * PWM_Init(): Initialize the PWM audio emulator.
 */
void PWM_Init(void)
{
	PWM_Mode = 0;
	PWM_Out_R = 0;
	PWM_Out_L = 0;
	
	memset(PWM_FIFO_R, 0x00, sizeof(PWM_FIFO_R));
	memset(PWM_FIFO_L, 0x00, sizeof(PWM_FIFO_L));
	
	PWM_RP_R = 0;
	PWM_WP_R = 0;
	PWM_RP_L = 0;
	PWM_WP_L = 0;
	PWM_Cycle_Tmp = 0;
	PWM_Int_Tmp = 0;
	PWM_FIFO_L_Tmp = 0;
	PWM_FIFO_R_Tmp = 0;
	
	PWM_Loudness = -2;
	PWM_Set_Cycle(0);
	PWM_Set_Int(0);
}


void PWM_Recalc_Scale(void)
{
	PWM_Offset = (PWM_Cycle / 2) + 1;
	PWM_Scale = 0x7FFF00 / PWM_Offset;
}


void PWM_Set_Cycle(unsigned int cycle)
{
	cycle--;
	PWM_Cycle = (cycle & 0xFFF);
	PWM_Cycle_Cnt = PWM_Cycles;
	
	PWM_Recalc_Scale();
}


void PWM_Set_Int(unsigned int int_time)
{
	int_time &= 0x0F;
	if (int_time)
		PWM_Int = PWM_Int_Cnt = int_time;
	else
		PWM_Int = PWM_Int_Cnt = 16;
}


void PWM_Clear_Timer(void)
{
	PWM_Cycle_Cnt = 0;
}


/**
 * PWM_SHIFT(): Shift PWM data.
 * @param src: Channel (L or R) with the source data.
 * @param dest Channel (L or R) for the destination.
 */
#define PWM_SHIFT(src, dest)										\
{													\
	/* Make sure the source FIFO isn't empty. */							\
	if (PWM_RP_##src != PWM_WP_##src)								\
	{												\
		/* Get destination channel output from the source channel FIFO. */			\
		PWM_Out_##dest = PWM_FIFO_##src[PWM_RP_##src];						\
													\
		/* Increment the source channel read pointer, resetting to 0 if it overflows. */	\
		PWM_RP_##src = (PWM_RP_##src + 1) & (PWM_BUF_SIZE - 1);					\
	}												\
}


static void PWM_Shift_Data(void)
{
	switch (PWM_Mode & 0x0F)
	{
		case 0x01:
		case 0x0D:
			// Rx_LL: Right -> Ignore, Left -> Left
			PWM_SHIFT(L, L);
			break;
		
		case 0x02:
		case 0x0E:
			// Rx_LR: Right -> Ignore, Left -> Right
			PWM_SHIFT(L, R);
			break;
		
		case 0x04:
		case 0x07:
			// RL_Lx: Right -> Left, Left -> Ignore
			PWM_SHIFT(R, L);
			break;
		
		case 0x05:
		case 0x09:
			// RR_LL: Right -> Right, Left -> Left
			PWM_SHIFT(L, L);
			PWM_SHIFT(R, R);
			break;
		
		case 0x06:
		case 0x0A:
			// RL_LR: Right -> Left, Left -> Right
			PWM_SHIFT(L, R);
			PWM_SHIFT(R, L);
			break;
		
		case 0x08:
		case 0x0B:
			// RR_Lx: Right -> Right, Left -> Ignore
			PWM_SHIFT(R, R);
			break;
		
		case 0x00:
		case 0x03:
		case 0x0C:
		case 0x0F:
		default:
			// Rx_Lx: Right -> Ignore, Left -> Ignore
			break;
	}
}


void PWM_Update_Timer(unsigned int cycle)
{
	// Don't do anything if PWM is disabled in the Sound menu.
	// FIXME: This seems to cause some games to freeze.
	if (!PWM_Enable)
		return;
	
	// Don't do anything if PWM isn't active.
	if ((PWM_Mode & 0x0F) == 0x00)
		return;
	
	if (PWM_Cycle == 0x00 || (PWM_Cycle_Cnt > cycle))
		return;
	
	PWM_Shift_Data();
	
	PWM_Cycle_Cnt += PWM_Cycle;
	
	PWM_Int_Cnt--;
	if (PWM_Int_Cnt == 0)
	{
		PWM_Int_Cnt = PWM_Int;
		
		if (PWM_Mode & 0x0080)
		{
			// RPT => generate DREQ1 as well as INT
			SH2_DMA1_Request(&M_SH2, 1);
			SH2_DMA1_Request(&S_SH2, 1);
		}
		if (_32X_MINT & 1)
			SH2_Interrupt(&M_SH2, 6);
		if (_32X_SINT & 1)
			SH2_Interrupt(&S_SH2, 6);
	}
}


void PWM_Update(int **buf, int length)
{
	if (!PWM_Enable)
		return;
	
	// New PWM scaling algorithm provided by Chilly Willy on the Sonic Retro forums.
	int tmpOutL = (((int)(PWM_Out_L & 0xFFF) - PWM_Offset) * PWM_Scale) >> (8 - PWM_Loudness);
	int tmpOutR = (((int)(PWM_Out_R & 0xFFF) - PWM_Offset) * PWM_Scale) >> (8 - PWM_Loudness);
	
	// Multiply PWM by 4 so it's audible.
	tmpOutL <<= 2;
	tmpOutR <<= 2;
	
	while (length > 0)
	{
		if (PWM_Out_L)
			buf[0][length-1] += tmpOutL;
		if (PWM_Out_R)
			buf[1][length-1] += tmpOutR;
		
		length--;
	}
}
I'll post it over at SonicRetro in the "official" Gens/GS thread when SonicRetro start responding again - it's mostly donw at the moment. :x

When i sent my patches to the author, all but one of the changes were made. Unfortunately, the one not made was the one that sets the DREQ line for the audio! :lol:

The above file also sets the PWM loudness to -2 as 0 is WAY too loud. -2 doesn't clip like 0 does, giving much cleaner sound.

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Wed Jul 21, 2010 7:59 am

I ran into something interesting today. I've been working (slowly) on sfx/midi code for the DMA PWM audio. While testing the sfx code, I noticed the channels were backwards. I triple checked the code and connections to the TV (using standard stereo composite cable), and couldn't find a problem. So I tried the same code on my CDX + 32X and the channels were fine.

Could it be the cable is wired wrong internally? Could it be the Model 2 vs the CDX? Has anyone else run into something like this?

Given the issue, I'll probably include a setting in the final code that allows the channels to be swapped in case someone has a similar problem.

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Wed Jul 21, 2010 5:23 pm

What model MD2 you got ?

VA0 and VA1 MD2s have 32X signals connected the right way, aswell as all MD1 models.
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Wed Jul 21, 2010 7:32 pm

No idea - never opened it up. :lol:

I think what I might do is try the Mars Diagnostic Cart (via the flash cart) and see if it has the same issue. Given how cheap the AV cables are (got them on eBay), it wouldn't surprise me any if that were the issue. I can test that by switching cables on the two.

Post Reply