Beispiel #1
0
// This happens at 4 khz, since 32 bytes at 4khz = 4 bytes at 32 khz (16bit stereo pcm)
void UpdateAudioDMA()
{
	static short zero_samples[8*2] = { 0 };
	if (g_audioDMA.AudioDMAControl.Enable)
	{
		// Read audio at g_audioDMA.current_source_address in RAM and push onto an
		// external audio fifo in the emulator, to be mixed with the disc
		// streaming output.
		g_audioDMA.remaining_blocks_count--;
		g_audioDMA.current_source_address += 32;

		if (g_audioDMA.remaining_blocks_count == 0)
		{
			g_audioDMA.current_source_address = g_audioDMA.SourceAddress;
			g_audioDMA.remaining_blocks_count = g_audioDMA.AudioDMAControl.NumBlocks;

			if (g_audioDMA.AudioDMAControl.NumBlocks == 0)
			{
				g_audioDMA.AudioDMAControl.Enable = 0;
			}
			else
			{
				// We make the samples ready as soon as possible
				void *address = Memory::GetPointer(g_audioDMA.SourceAddress);
				AudioCommon::SendAIBuffer((short*)address, g_audioDMA.AudioDMAControl.NumBlocks * 8);
			}
			GenerateDSPInterrupt(DSP::INT_AID);
		}
	}
	else
	{
		AudioCommon::SendAIBuffer(&zero_samples[0], 8);
	}
}
Beispiel #2
0
// This happens at 4 khz, since 32 bytes at 4khz = 4 bytes at 32 khz (16bit stereo pcm)
void UpdateAudioDMA()
{
	if (g_audioDMA.AudioDMAControl.Enable && g_audioDMA.BlocksLeft)
	{
		// Read audio at g_audioDMA.ReadAddress in RAM and push onto an
		// external audio fifo in the emulator, to be mixed with the disc
		// streaming output. If that audio queue fills up, we delay the
		// emulator.

		g_audioDMA.BlocksLeft--;
		g_audioDMA.ReadAddress += 32;

		if (g_audioDMA.BlocksLeft == 0)
		{
			dsp_emulator->DSP_SendAIBuffer(g_audioDMA.SourceAddress, 8*g_audioDMA.AudioDMAControl.NumBlocks);
			GenerateDSPInterrupt(DSP::INT_AID);
			g_audioDMA.BlocksLeft = g_audioDMA.AudioDMAControl.NumBlocks;
			g_audioDMA.ReadAddress = g_audioDMA.SourceAddress;
		}
	}
	else
	{
		// Send silence. Yeah, it's a bit of a waste to sample rate convert
		// silence.  or hm. Maybe we shouldn't do this :)
		dsp_emulator->DSP_SendAIBuffer(0, AudioInterface::GetAIDSampleRate());
	}
}
Beispiel #3
0
void Do_ARAM_DMA()
{
	if (g_arDMA.Cnt.count == 32)
	{
		// Beyond Good and Evil (GGEE41) sends count 32
		// Lost Kingdoms 2 needs the exception check here in DSP HLE mode
		GenerateDSPInterrupt(INT_ARAM);
		CoreTiming::ForceExceptionCheck(100);
	}
	else
	{
		g_dspState.DSPControl.DMAState = 1;
		CoreTiming::ScheduleEvent_Threadsafe(0, et_GenerateDSPInterrupt, INT_ARAM | (1<<16));

		// Force an early exception check on large transfers. Fixes RE2 audio.
		// NFS:HP2 (<= 6144)
		// Viewtiful Joe (<= 6144)
		// Sonic Mega Collection (> 2048)
		// Paper Mario battles (> 32)
		// Mario Super Baseball (> 32)
		// Knockout Kings 2003 loading (> 32)
		// WWE DOR (> 32)
		if (g_arDMA.Cnt.count > 2048 && g_arDMA.Cnt.count <= 6144)
			CoreTiming::ForceExceptionCheck(100);
	}

	// Real hardware DMAs in 32byte chunks, but we can get by with 8byte chunks
	if (g_arDMA.Cnt.dir)
	{
		// ARAM -> MRAM
		INFO_LOG(DSPINTERFACE, "DMA %08x bytes from ARAM %08x to MRAM %08x PC: %08x",
			g_arDMA.Cnt.count, g_arDMA.ARAddr, g_arDMA.MMAddr, PC);

		// Outgoing data from ARAM is mirrored every 64MB (verified on real HW)
		g_arDMA.ARAddr &= 0x3ffffff;
		g_arDMA.MMAddr &= 0x3ffffff;

		if (g_arDMA.ARAddr < g_ARAM.size)
		{
			while (g_arDMA.Cnt.count)
			{
				// These are logically seperated in code to show that a memory map has been set up
				// See below in the write section for more information
				if ((g_ARAM_Info.Hex & 0xf) == 3)
				{
					Memory::Write_U64_Swap(*(u64*)&g_ARAM.ptr[g_arDMA.ARAddr & g_ARAM.mask], g_arDMA.MMAddr);
				}
				else if ((g_ARAM_Info.Hex & 0xf) == 4)
				{
					Memory::Write_U64_Swap(*(u64*)&g_ARAM.ptr[g_arDMA.ARAddr & g_ARAM.mask], g_arDMA.MMAddr);
				}
				else
				{
					Memory::Write_U64_Swap(*(u64*)&g_ARAM.ptr[g_arDMA.ARAddr & g_ARAM.mask], g_arDMA.MMAddr);
				}

				g_arDMA.MMAddr += 8;
				g_arDMA.ARAddr += 8;
				g_arDMA.Cnt.count -= 8;
			}
		}
		else
		{
			// Assuming no external ARAM installed; returns zeroes on out of bounds reads (verified on real HW)
			while (g_arDMA.Cnt.count)
			{
				Memory::Write_U64(0, g_arDMA.MMAddr);
				g_arDMA.MMAddr += 8;
				g_arDMA.ARAddr += 8;
				g_arDMA.Cnt.count -= 8;
			}
		}
	}
	else
	{
		// MRAM -> ARAM
		INFO_LOG(DSPINTERFACE, "DMA %08x bytes from MRAM %08x to ARAM %08x PC: %08x",
			g_arDMA.Cnt.count, g_arDMA.MMAddr, g_arDMA.ARAddr, PC);

		// Incoming data into ARAM is mirrored every 64MB (verified on real HW)
		g_arDMA.ARAddr &= 0x3ffffff;
		g_arDMA.MMAddr &= 0x3ffffff;

		if (g_arDMA.ARAddr < g_ARAM.size)
		{
			while (g_arDMA.Cnt.count)
			{
				if ((g_ARAM_Info.Hex & 0xf) == 3)
				{
					*(u64*)&g_ARAM.ptr[g_arDMA.ARAddr & g_ARAM.mask] = Common::swap64(Memory::Read_U64(g_arDMA.MMAddr));
				}
				else if ((g_ARAM_Info.Hex & 0xf) == 4)
				{
					if (g_arDMA.ARAddr < 0x400000)
					{
						*(u64*)&g_ARAM.ptr[(g_arDMA.ARAddr + 0x400000) & g_ARAM.mask] = Common::swap64(Memory::Read_U64(g_arDMA.MMAddr));
					}
					*(u64*)&g_ARAM.ptr[g_arDMA.ARAddr & g_ARAM.mask] = Common::swap64(Memory::Read_U64(g_arDMA.MMAddr));
				}
				else
				{
					*(u64*)&g_ARAM.ptr[g_arDMA.ARAddr & g_ARAM.mask] = Common::swap64(Memory::Read_U64(g_arDMA.MMAddr));
				}

				g_arDMA.MMAddr += 8;
				g_arDMA.ARAddr += 8;
				g_arDMA.Cnt.count -= 8;
			}
		}
		else
		{
			// Assuming no external ARAM installed; writes nothing to ARAM when out of bounds (verified on real HW)
			g_arDMA.MMAddr += g_arDMA.Cnt.count;
			g_arDMA.ARAddr += g_arDMA.Cnt.count;
			g_arDMA.Cnt.count = 0;
		}
	}
}
Beispiel #4
0
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
{
	// Declare all the boilerplate direct MMIOs.
	struct {
		u32 addr;
		u16* ptr;
		bool align_writes_on_32_bytes;
	} directly_mapped_vars[] = {
		{ AR_INFO, &g_ARAM_Info.Hex },
		{ AR_MODE, &g_AR_MODE },
		{ AR_REFRESH, &g_AR_REFRESH },
		{ AR_DMA_MMADDR_H, MMIO::Utils::HighPart(&g_arDMA.MMAddr) },
		{ AR_DMA_MMADDR_L, MMIO::Utils::LowPart(&g_arDMA.MMAddr), true },
		{ AR_DMA_ARADDR_H, MMIO::Utils::HighPart(&g_arDMA.ARAddr) },
		{ AR_DMA_ARADDR_L, MMIO::Utils::LowPart(&g_arDMA.ARAddr), true },
		{ AR_DMA_CNT_H, MMIO::Utils::HighPart(&g_arDMA.Cnt.Hex) },
		// AR_DMA_CNT_L triggers DMA
		{ AUDIO_DMA_START_HI, MMIO::Utils::HighPart(&g_audioDMA.SourceAddress) },
		{ AUDIO_DMA_START_LO, MMIO::Utils::LowPart(&g_audioDMA.SourceAddress) },
	};
	for (auto& mapped_var : directly_mapped_vars)
	{
		u16 write_mask = mapped_var.align_writes_on_32_bytes ? 0xFFE0 : 0xFFFF;
		mmio->Register(base | mapped_var.addr,
			MMIO::DirectRead<u16>(mapped_var.ptr),
			MMIO::DirectWrite<u16>(mapped_var.ptr, write_mask)
		);
	}

	// DSP mail MMIOs call DSP emulator functions to get results or write data.
	mmio->Register(base | DSP_MAIL_TO_DSP_HI,
		MMIO::ComplexRead<u16>([](u32) {
			if (dsp_slice > DSP_MAIL_SLICE && dsp_is_lle)
			{
				dsp_emulator->DSP_Update(DSP_MAIL_SLICE);
				dsp_slice -= DSP_MAIL_SLICE;
			}
			return dsp_emulator->DSP_ReadMailBoxHigh(true);
		}),
		MMIO::ComplexWrite<u16>([](u32, u16 val) {
			dsp_emulator->DSP_WriteMailBoxHigh(true, val);
		})
	);
	mmio->Register(base | DSP_MAIL_TO_DSP_LO,
		MMIO::ComplexRead<u16>([](u32) {
			return dsp_emulator->DSP_ReadMailBoxLow(true);
		}),
		MMIO::ComplexWrite<u16>([](u32, u16 val) {
			dsp_emulator->DSP_WriteMailBoxLow(true, val);
		})
	);
	mmio->Register(base | DSP_MAIL_FROM_DSP_HI,
		MMIO::ComplexRead<u16>([](u32) {
			if (dsp_slice > DSP_MAIL_SLICE && dsp_is_lle)
			{
				dsp_emulator->DSP_Update(DSP_MAIL_SLICE);
				dsp_slice -= DSP_MAIL_SLICE;
			}
			return dsp_emulator->DSP_ReadMailBoxHigh(false);
		}),
		MMIO::InvalidWrite<u16>()
	);
	mmio->Register(base | DSP_MAIL_FROM_DSP_LO,
		MMIO::ComplexRead<u16>([](u32) {
			return dsp_emulator->DSP_ReadMailBoxLow(false);
		}),
		MMIO::InvalidWrite<u16>()
	);

	mmio->Register(base | DSP_CONTROL,
		MMIO::ComplexRead<u16>([](u32) {
			return (g_dspState.DSPControl.Hex & ~DSP_CONTROL_MASK) |
			       (dsp_emulator->DSP_ReadControlRegister() & DSP_CONTROL_MASK);
		}),
		MMIO::ComplexWrite<u16>([](u32, u16 val) {
			UDSPControl tmpControl;
			tmpControl.Hex = (val & ~DSP_CONTROL_MASK) |
				(dsp_emulator->DSP_WriteControlRegister(val) & DSP_CONTROL_MASK);

			// Not really sure if this is correct, but it works...
			// Kind of a hack because DSP_CONTROL_MASK should make this bit
			// only viewable to dsp emulator
			if (val & 1 /*DSPReset*/)
			{
				g_audioDMA.AudioDMAControl.Hex = 0;
			}

			// Update DSP related flags
			g_dspState.DSPControl.DSPReset     = tmpControl.DSPReset;
			g_dspState.DSPControl.DSPAssertInt = tmpControl.DSPAssertInt;
			g_dspState.DSPControl.DSPHalt      = tmpControl.DSPHalt;
			g_dspState.DSPControl.DSPInit      = tmpControl.DSPInit;

			// Interrupt (mask)
			g_dspState.DSPControl.AID_mask  = tmpControl.AID_mask;
			g_dspState.DSPControl.ARAM_mask = tmpControl.ARAM_mask;
			g_dspState.DSPControl.DSP_mask  = tmpControl.DSP_mask;

			// Interrupt
			if (tmpControl.AID)  g_dspState.DSPControl.AID  = 0;
			if (tmpControl.ARAM) g_dspState.DSPControl.ARAM = 0;
			if (tmpControl.DSP)  g_dspState.DSPControl.DSP  = 0;

			// unknown
			g_dspState.DSPControl.DSPInitCode = tmpControl.DSPInitCode;
			g_dspState.DSPControl.pad  = tmpControl.pad;
			if (g_dspState.DSPControl.pad != 0)
			{
				PanicAlert("DSPInterface (w) g_dspState.DSPControl (CC00500A) gets a value with junk in the padding %08x", val);
			}

			UpdateInterrupts();
		})
	);

	// ARAM MMIO controlling the DMA start.
	mmio->Register(base | AR_DMA_CNT_L,
		MMIO::DirectRead<u16>(MMIO::Utils::LowPart(&g_arDMA.Cnt.Hex)),
		MMIO::ComplexWrite<u16>([](u32, u16 val) {
			g_arDMA.Cnt.Hex = (g_arDMA.Cnt.Hex & 0xFFFF0000) | (val & ~31);
			Do_ARAM_DMA();
		})
	);

	// Audio DMA MMIO controlling the DMA start.
	mmio->Register(base | AUDIO_DMA_CONTROL_LEN,
		MMIO::DirectRead<u16>(&g_audioDMA.AudioDMAControl.Hex),
		MMIO::ComplexWrite<u16>([](u32, u16 val) {
			bool already_enabled = g_audioDMA.AudioDMAControl.Enable;
			g_audioDMA.AudioDMAControl.Hex = val;

			// Only load new values if were not already doing a DMA transfer,
			// otherwise just let the new values be autoloaded in when the
			// current transfer ends.
			if (!already_enabled && g_audioDMA.AudioDMAControl.Enable)
			{
				g_audioDMA.current_source_address = g_audioDMA.SourceAddress;
				g_audioDMA.remaining_blocks_count = g_audioDMA.AudioDMAControl.NumBlocks;

				// We make the samples ready as soon as possible
				void *address = Memory::GetPointer(g_audioDMA.SourceAddress);
				AudioCommon::SendAIBuffer((short*)address, g_audioDMA.AudioDMAControl.NumBlocks * 8);

				GenerateDSPInterrupt(DSP::INT_AID);
			}
		})
	);

	// Audio DMA blocks remaining is invalid to write to, and requires logic on
	// the read side.
	mmio->Register(base | AUDIO_DMA_BLOCKS_LEFT,
		MMIO::DirectRead<u16>(&g_audioDMA.remaining_blocks_count),
		MMIO::InvalidWrite<u16>()
	);

	// 32 bit reads/writes are a combination of two 16 bit accesses.
	for (int i = 0; i < 0x1000; i += 4)
	{
		mmio->Register(base | i,
			MMIO::ReadToSmaller<u32>(mmio, base | i, base | (i + 2)),
			MMIO::WriteToSmaller<u32>(mmio, base | i, base | (i + 2))
		);
	}
}