summaryrefslogtreecommitdiff
path: root/Source/Core/DSPCore/Src/DSPAccelerator.cpp
blob: d4edcbbacffb335af21d3ccc4ae5652fb397aa73 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// Copyright (C) 2003 Dolphin Project.

// 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, version 2.0.

// 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 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/

#include "Common.h"

#include "DSPCore.h"
#include "DSPHost.h"
#include "DSPHWInterface.h"
#include "DSPInterpreter.h"

// The hardware adpcm decoder :)
s16 ADPCM_Step(u32& _rSamplePos)
{
	const s16 *pCoefTable = (const s16 *)&g_dsp.ifx_regs[DSP_COEF_A1_0];

	if (((_rSamplePos) & 15) == 0)
	{
		g_dsp.ifx_regs[DSP_PRED_SCALE] = DSPHost_ReadHostMemory((_rSamplePos & ~15) >> 1);
		_rSamplePos += 2;
	}

	int scale = 1 << (g_dsp.ifx_regs[DSP_PRED_SCALE] & 0xF);
	int coef_idx = g_dsp.ifx_regs[DSP_PRED_SCALE] >> 4;

	s32 coef1 = pCoefTable[coef_idx * 2 + 0];
	s32 coef2 = pCoefTable[coef_idx * 2 + 1];

	int temp = (_rSamplePos & 1) ?
		   (DSPHost_ReadHostMemory(_rSamplePos >> 1) & 0xF) :
		   (DSPHost_ReadHostMemory(_rSamplePos >> 1) >> 4);

	if (temp >= 8)
		temp -= 16;

	// 0x400 = 0.5  in 11-bit fixed point
	int val = (scale * temp) + ((0x400 + coef1 * (s16)g_dsp.ifx_regs[DSP_YN1] + coef2 * (s16)g_dsp.ifx_regs[DSP_YN2]) >> 11);

	// Clamp values.
	if (val > 0x7FFF)
		val = 0x7FFF;
	else if (val < -0x7FFF)
		val = -0x7FFF;

	g_dsp.ifx_regs[DSP_YN2] = g_dsp.ifx_regs[DSP_YN1];
	g_dsp.ifx_regs[DSP_YN1] = val;

	_rSamplePos++;

    // The advanced interpolation (linear, polyphase,...) is done by the UCode,
	// so we don't need to bother with it here.
	return val;
}

u16 dsp_read_aram_d3()
{
	// Zelda ucode reads ARAM through 0xffd3.
	const u32 EndAddress = (g_dsp.ifx_regs[DSP_ACEAH] << 16) | g_dsp.ifx_regs[DSP_ACEAL];
	u32 Address = (g_dsp.ifx_regs[DSP_ACCAH] << 16) | g_dsp.ifx_regs[DSP_ACCAL];
	u16 val = 0;
	switch (g_dsp.ifx_regs[DSP_FORMAT]) {
		case 0x5:   // u8 reads
			val = DSPHost_ReadHostMemory(Address);
			Address++;
			break;
		case 0x6:   // u16 reads
		    val = (DSPHost_ReadHostMemory(Address*2) << 8) | DSPHost_ReadHostMemory(Address*2 + 1);
			Address++;
			break;
		default:
			ERROR_LOG(DSPLLE, "dsp_read_aram_d3: Unseen Format %i", g_dsp.ifx_regs[DSP_FORMAT]);
			break;
	}
	if (Address >= EndAddress) 
	{
		// Set address back to start address.
		Address = (g_dsp.ifx_regs[DSP_ACSAH] << 16) | g_dsp.ifx_regs[DSP_ACSAL];
	}
	g_dsp.ifx_regs[DSP_ACCAH] = Address >> 16;
	g_dsp.ifx_regs[DSP_ACCAL] = Address & 0xffff;
	return val;
}

void dsp_write_aram_d3(u16 value)
{
	// Zelda ucode writes a bunch of zeros to ARAM through d3 during
	// initialization.  Don't know if it ever does it later, too.
	// const u32 EndAddress = (g_dsp.ifx_regs[DSP_ACEAH] << 16) | g_dsp.ifx_regs[DSP_ACEAL]; // Unused?
	u32 Address = (g_dsp.ifx_regs[DSP_ACCAH] << 16) | g_dsp.ifx_regs[DSP_ACCAL];
	switch (g_dsp.ifx_regs[DSP_FORMAT]) {
		case 0xA:   // u16 writes
			DSPHost_WriteHostMemory(value >> 8, Address*2);
			DSPHost_WriteHostMemory(value & 0xFF, Address*2 + 1);
			Address++;
			break;
		default:
			ERROR_LOG(DSPLLE, "dsp_write_aram_d3: Unseen Format %i", g_dsp.ifx_regs[DSP_FORMAT]);
			break;
	}
	g_dsp.ifx_regs[DSP_ACCAH] = Address >> 16;
	g_dsp.ifx_regs[DSP_ACCAL] = Address & 0xffff;
}

u16 dsp_read_accelerator()
{
	const u32 EndAddress = (g_dsp.ifx_regs[DSP_ACEAH] << 16) | g_dsp.ifx_regs[DSP_ACEAL];
	u32 Address = (g_dsp.ifx_regs[DSP_ACCAH] << 16) | g_dsp.ifx_regs[DSP_ACCAL];

	u16 val;

	// let's do the "hardware" decode DSP_FORMAT is interesting - the Zelda
	// ucode seems to indicate that the bottom two bits specify the "read size"
	// and the address multiplier.  The bits above that may be things like sign
	// extention and do/do not use ADPCM.  It also remains to be figured out
	// whether there's a difference between the usual accelerator "read
	// address" and 0xd3.
	switch (g_dsp.ifx_regs[DSP_FORMAT])
	{
	    case 0x00:  // ADPCM audio
		    val = ADPCM_Step(Address);
		    break;
	    case 0x0A:  // 16-bit PCM audio
		    val = (DSPHost_ReadHostMemory(Address*2) << 8) | DSPHost_ReadHostMemory(Address*2 + 1);
		    g_dsp.ifx_regs[DSP_YN2] = g_dsp.ifx_regs[DSP_YN1];
		    g_dsp.ifx_regs[DSP_YN1] = val;
		    Address++;
		    break;
	    case 0x19:  // 8-bit PCM audio
		    val = DSPHost_ReadHostMemory(Address) << 8; // probably wrong
		    g_dsp.ifx_regs[DSP_YN2] = g_dsp.ifx_regs[DSP_YN1];
		    g_dsp.ifx_regs[DSP_YN1] = val;
		    Address++;
			break;
	    default:
		    ERROR_LOG(DSPLLE, "Unknown DSP Format %x", g_dsp.ifx_regs[DSP_FORMAT]);
		    val = 0;
	}

	// TODO: Take GAIN into account, whatever it is.
	// adpcm = 0, pcm8 = 0x100, pcm16 = 0x800
	// games using pcm8 : Phoenix Wright Ace Attorney (Wiiware), Megaman 9-10 (WiiWare)
	if (g_dsp.ifx_regs[DSP_GAIN] > 0)
	{
		//NOTICE_LOG(DSPLLE,"format: 0x%04x - val: 0x%04x - gain: 0x%04x", g_dsp.ifx_regs[DSP_FORMAT], val, g_dsp.ifx_regs[DSP_GAIN]);
	}

	// Check for loop.
	if (Address >= EndAddress)
	{
		// Set address back to start address.
		Address = (g_dsp.ifx_regs[DSP_ACSAH] << 16) | g_dsp.ifx_regs[DSP_ACSAL];

		// Do we really need both? (nakee: seems to cause problems with some
		// AX games)
		//		DSPHost_InterruptRequest();
		//		DSPCore_SetException(EXP_2);
		DSPCore_SetException(EXP_ACCOV);

		// Somehow, YN1 and YN2 must be initialized with their "loop" values,
		// so yeah, it seems likely that we should raise an exception to let
		// the DSP program do that, at least if DSP_FORMAT == 0x0A.
	}
	//else
	//	DSPCore_SetException(EXP_6); // test! (bunch of NOPs there - helps "SMB S&R (wii)")

	g_dsp.ifx_regs[DSP_ACCAH] = Address >> 16;
	g_dsp.ifx_regs[DSP_ACCAL] = Address & 0xffff;
	return val;
}