mirror of https://github.com/xemu-project/xemu.git
mcpx: Enable APU pipeline
This commit is contained in:
parent
9004009bb3
commit
416e12c3b3
|
@ -45,6 +45,7 @@ jobs:
|
|||
7z x -y msys64.7z "-oC:\tools\"
|
||||
echo "Updating MSYS2 environment..."
|
||||
C:\tools\msys64\usr\bin\bash.exe -lc "pacman -Syu --noconfirm"
|
||||
C:\tools\msys64\usr\bin\bash.exe -lc "pacman --needed --noconfirm -S mingw-w64-x86_64-libsamplerate"
|
||||
# - name: Initialize Compiler Cache
|
||||
# id: cache
|
||||
# uses: actions/cache@v1
|
||||
|
@ -99,6 +100,7 @@ jobs:
|
|||
libgtk-3-dev \
|
||||
libpixman-1-dev \
|
||||
libsdl2-dev \
|
||||
libsamplerate0-dev \
|
||||
ccache
|
||||
- name: Initialize Compiler Cache
|
||||
id: cache
|
||||
|
@ -152,6 +154,7 @@ jobs:
|
|||
libepoxy \
|
||||
pixman \
|
||||
pkg-config \
|
||||
libsamplerate \
|
||||
sdl2
|
||||
- name: Initialize Compiler Cache
|
||||
id: cache
|
||||
|
|
|
@ -4564,6 +4564,15 @@ else
|
|||
feature_not_found "openssl" "Install openssl-dev"
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# libsamplerate probe
|
||||
if $pkg_config --exists samplerate; then
|
||||
QEMU_CFLAGS="$QEMU_CFLAGS $($pkg_config --cflags samplerate)"
|
||||
LIBS="$LIBS $($pkg_config --libs samplerate)"
|
||||
else
|
||||
feature_not_found "samplerate" "Install libsamplerate0-dev"
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# libxml2 probe
|
||||
if test "$libxml2" != "no" ; then
|
||||
|
|
|
@ -100,7 +100,8 @@ enum {
|
|||
#define GS_MINT (1<<7) /* ro */
|
||||
#define GS_POINT (1<<6) /* ro */
|
||||
#define GS_PIINT (1<<5) /* ro */
|
||||
#define GS_RSRVD ((1<<4)|(1<<3))
|
||||
#define GS_SOINT (1<<4) /* ro */
|
||||
#define GS_RSRVD (1<<3)
|
||||
#define GS_MOINT (1<<2) /* ro */
|
||||
#define GS_MIINT (1<<1) /* ro */
|
||||
#define GS_GSCI 1 /* rwc */
|
||||
|
@ -112,6 +113,7 @@ enum {
|
|||
GS_MINT| \
|
||||
GS_POINT| \
|
||||
GS_PIINT| \
|
||||
GS_SOINT| \
|
||||
GS_RSRVD| \
|
||||
GS_MOINT| \
|
||||
GS_MIINT)
|
||||
|
@ -176,7 +178,7 @@ enum {
|
|||
CAS = 0x34
|
||||
};
|
||||
|
||||
#define GET_BM(index) (((index) >> 4) & 3)
|
||||
#define GET_BM(index) (((index) >> 4) & 7)
|
||||
|
||||
static void po_callback (void *opaque, int free);
|
||||
static void pi_callback (void *opaque, int avail);
|
||||
|
@ -274,6 +276,9 @@ static void voice_set_active (AC97LinkState *s, int bm_index, int on)
|
|||
AUD_set_active_in (s->voice_mc, on);
|
||||
break;
|
||||
|
||||
case SO_INDEX:
|
||||
break;
|
||||
|
||||
default:
|
||||
AUD_log ("ac97", "invalid bm_index(%d) in voice_set_active\n", bm_index);
|
||||
break;
|
||||
|
@ -368,6 +373,9 @@ static void open_voice (AC97LinkState *s, int index, int freq)
|
|||
&as
|
||||
);
|
||||
break;
|
||||
|
||||
case SO_INDEX:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -387,6 +395,9 @@ static void open_voice (AC97LinkState *s, int index, int freq)
|
|||
AUD_close_in (&s->card, s->voice_mc);
|
||||
s->voice_mc = NULL;
|
||||
break;
|
||||
|
||||
case SO_INDEX:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -659,6 +670,7 @@ static void nam_writew (void *opaque, uint32_t addr, uint32_t val)
|
|||
default:
|
||||
dolog ("U nam writew %#x <- %#x\n", addr, val);
|
||||
mixer_store (s, index, val);
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -729,6 +741,7 @@ static uint32_t nabm_readb (void *opaque, uint32_t addr)
|
|||
break;
|
||||
default:
|
||||
dolog ("U nabm readb %#x -> %#x\n", addr, val);
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
|
@ -760,6 +773,7 @@ static uint32_t nabm_readw (void *opaque, uint32_t addr)
|
|||
break;
|
||||
default:
|
||||
dolog ("U nabm readw %#x -> %#x\n", addr, val);
|
||||
val = nabm_readb(opaque, addr) | (nabm_readb(opaque, addr + 1) << 8);
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
|
@ -809,6 +823,7 @@ static uint32_t nabm_readl (void *opaque, uint32_t addr)
|
|||
break;
|
||||
default:
|
||||
dolog ("U nabm readl %#x -> %#x\n", addr, val);
|
||||
val = nabm_readw(opaque, addr) | (nabm_readw(opaque, addr + 2) << 16);
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
|
@ -894,6 +909,8 @@ static void nabm_writew (void *opaque, uint32_t addr, uint32_t val)
|
|||
break;
|
||||
default:
|
||||
dolog ("U nabm writew %#x <- %#x\n", addr, val);
|
||||
nabm_writeb(opaque, addr, val & 0xff);
|
||||
nabm_writeb(opaque, addr + 1, (val >> 8) & 0xff);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -929,6 +946,8 @@ static void nabm_writel (void *opaque, uint32_t addr, uint32_t val)
|
|||
break;
|
||||
default:
|
||||
dolog ("U nabm writel %#x <- %#x\n", addr, val);
|
||||
nabm_writew(opaque, addr, val & 0xffff);
|
||||
nabm_writew(opaque, addr + 2, (val >> 16) & 0xffff);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,9 @@ obj-y += chihiro.o
|
|||
obj-y += xbox_pci.o acpi_xbox.o
|
||||
obj-y += amd_smbus.o smbus_xbox_smc.o smbus_cx25871.o smbus_adm1032.o smbus_storage.o eeprom_generation.o
|
||||
obj-y += nvnet.o
|
||||
obj-y += mcpx_apu.o mcpx_aci.o
|
||||
obj-y += lpc47m157.o
|
||||
obj-y += xid.o
|
||||
obj-y += chihiro-usb.o
|
||||
|
||||
obj-y += dsp/
|
||||
obj-y += nv2a/
|
||||
obj-y += mcpx/
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* ADPCM decoder
|
||||
*
|
||||
* Copyright (c) 2017 Jannik Vogel
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
// See https://wiki.multimedia.cx/index.php/IMA_ADPCM for more information
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
static int8_t ima_index_table[16] = {
|
||||
-1, -1, -1, -1, 2, 4, 6, 8,
|
||||
-1, -1, -1, -1, 2, 4, 6, 8
|
||||
};
|
||||
|
||||
static uint16_t ima_step_table[89] = {
|
||||
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
|
||||
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
||||
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
|
||||
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
|
||||
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
|
||||
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
|
||||
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int32_t predictor;
|
||||
int8_t step_index;
|
||||
uint16_t step;
|
||||
} ADPCMDecoder;
|
||||
|
||||
static void adpcm_decoder_initialize(ADPCMDecoder* d, int16_t predictor, int8_t step_index) {
|
||||
d->predictor = predictor;
|
||||
d->step_index = step_index;
|
||||
}
|
||||
|
||||
// The upper portion of the `nibble` argument is ignored.
|
||||
static int16_t adpcm_decoder_step(ADPCMDecoder* d, uint8_t nibble) {
|
||||
|
||||
// Get step and prepare index for next sample
|
||||
if (d->step_index < 0) {
|
||||
d->step_index = 0;
|
||||
} else if (d->step_index > 88) {
|
||||
d->step_index = 88;
|
||||
}
|
||||
d->step = ima_step_table[d->step_index];
|
||||
d->step_index += ima_index_table[nibble & 0xF];
|
||||
|
||||
// Calculate diff
|
||||
int32_t diff = d->step >> 3;
|
||||
if (nibble & 1) {
|
||||
diff += d->step >> 2;
|
||||
}
|
||||
if (nibble & 2) {
|
||||
diff += d->step >> 1;
|
||||
}
|
||||
if (nibble & 4) {
|
||||
diff += d->step;
|
||||
}
|
||||
if (nibble & 8) {
|
||||
diff = -diff;
|
||||
}
|
||||
|
||||
// Update predictor and clamp to signed 16 bit
|
||||
d->predictor += diff;
|
||||
if (d->predictor < -0x8000) {
|
||||
d->predictor = -0x8000;
|
||||
} else if (d->predictor > 0x7FFF) {
|
||||
d->predictor = 0x7FFF;
|
||||
}
|
||||
|
||||
return d->predictor;
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* ADPCM decoder
|
||||
*
|
||||
* Copyright (c) 2017 Jannik Vogel
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "adpcm.h"
|
||||
|
||||
static int16_t adpcm_decode_block_setup(ADPCMDecoder* decoder, uint32_t word) {
|
||||
int16_t predictor = word & 0xFFFF;
|
||||
uint8_t step_index = (word >> 16) & 0xFF;
|
||||
adpcm_decoder_initialize(decoder, predictor, step_index);
|
||||
return predictor;
|
||||
}
|
||||
|
||||
static int16_t* adpcm_decode_word(ADPCMDecoder* decoder, int16_t* samples, uint32_t word, int first, int last) {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if (i >= first) {
|
||||
samples++;
|
||||
}
|
||||
if (i <= last) {
|
||||
*samples = adpcm_decoder_step(decoder, word);
|
||||
word >>= 4;
|
||||
}
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
// For stereo we decode 2x 32 bit each iteration (as 32 bits).
|
||||
static void adpcm_decode_stereo_block(int16_t* samples_l, int16_t* samples_r, const uint8_t* data, unsigned int first, unsigned int last) {
|
||||
uint32_t* word = (uint32_t*)data;
|
||||
ADPCMDecoder decoder_l;
|
||||
ADPCMDecoder decoder_r;
|
||||
*samples_l = adpcm_decode_block_setup(&decoder_l, *word++);
|
||||
*samples_r = adpcm_decode_block_setup(&decoder_r, *word++);
|
||||
for(unsigned int i = 0; i < 8; i++) {
|
||||
for(unsigned int j = 0; j < 2; j++) {
|
||||
if (j == 0) {
|
||||
samples_l = adpcm_decode_word(&decoder_l, samples_l, *word++, first, last);
|
||||
} else {
|
||||
samples_r = adpcm_decode_word(&decoder_r, samples_r, *word++, first, last);
|
||||
}
|
||||
}
|
||||
first -= 8;
|
||||
last -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
// For mono we decode 32 bit at once in each iteration.
|
||||
// We could do 64 bits here, but if we parallelize this algorithm (later) we
|
||||
// would limit ourselves to 64 bit operands. However, most of ADPCM is 16 to
|
||||
// 32 bits (for overflows). So we stick with 32 bit and should even consider
|
||||
// going back to 16 bit (if enough decoders run at once)!
|
||||
static void adpcm_decode_mono_block(int16_t* samples, const uint8_t* data, unsigned int first, unsigned int last) {
|
||||
uint32_t* word = (uint32_t*)data;
|
||||
ADPCMDecoder decoder;
|
||||
*samples = adpcm_decode_block_setup(&decoder, *word++);
|
||||
for(unsigned int i = 0; i < 8; i++) {
|
||||
samples = adpcm_decode_word(&decoder, samples, *word++, first, last);
|
||||
first -= 8;
|
||||
last -= 8;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
obj-y += apu.o aci.o
|
||||
apu.o-cflags := $(SDL_CFLAGS)
|
||||
|
||||
obj-y += dsp/
|
|
@ -2,7 +2,7 @@
|
|||
* QEMU MCPX Audio Codec Interface implementation
|
||||
*
|
||||
* Copyright (c) 2012 espes
|
||||
* Copyright (c) 2020 Matt Borgerson
|
||||
* Copyright (c) 2020-2021 Matt Borgerson
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* ADPCM decoder from the ADPCM-XQ project: https://github.com/dbry/adpcm-xq
|
||||
*
|
||||
* Copyright (c) David Bryant
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Conifer Software nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef ADPCM_DECODE_H
|
||||
#define ADPCM_DECODE_H
|
||||
|
||||
/********************************* 4-bit ADPCM decoder ********************************/
|
||||
|
||||
/* Decode the block of ADPCM data into PCM. This requires no context because ADPCM blocks
|
||||
* are indeppendently decodable. This assumes that a single entire block is always decoded;
|
||||
* it must be called multiple times for multiple blocks and cannot resume in the middle of a
|
||||
* block.
|
||||
*
|
||||
* Parameters:
|
||||
* outbuf destination for interleaved PCM samples
|
||||
* inbuf source ADPCM block
|
||||
* inbufsize size of source ADPCM block
|
||||
* channels number of channels in block (must be determined from other context)
|
||||
*
|
||||
* Returns number of converted composite samples (total samples divided by number of channels)
|
||||
*/
|
||||
|
||||
static int adpcm_decode_block (int16_t *outbuf, const uint8_t *inbuf, size_t inbufsize, int channels)
|
||||
{
|
||||
#define CLIP(data, min, max) \
|
||||
if ((data) > (max)) data = max; \
|
||||
else if ((data) < (min)) data = min;
|
||||
|
||||
/* step table */
|
||||
static const uint16_t step_table[89] = {
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
16, 17, 19, 21, 23, 25, 28, 31,
|
||||
34, 37, 41, 45, 50, 55, 60, 66,
|
||||
73, 80, 88, 97, 107, 118, 130, 143,
|
||||
157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658,
|
||||
724, 796, 876, 963, 1060, 1166, 1282, 1411,
|
||||
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
|
||||
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
|
||||
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
|
||||
32767
|
||||
};
|
||||
|
||||
/* step index tables */
|
||||
static const int index_table[] = {
|
||||
/* adpcm data size is 4 */
|
||||
-1, -1, -1, -1, 2, 4, 6, 8
|
||||
};
|
||||
|
||||
int ch, samples = 1, chunks;
|
||||
int32_t pcmdata[2];
|
||||
int8_t index[2];
|
||||
|
||||
if (inbufsize < (uint32_t) channels * 4)
|
||||
return 0;
|
||||
|
||||
for (ch = 0; ch < channels; ch++) {
|
||||
*outbuf++ = pcmdata[ch] = (int16_t) (inbuf [0] | (inbuf [1] << 8));
|
||||
index[ch] = inbuf [2];
|
||||
|
||||
if (index [ch] < 0 || index [ch] > 88 || inbuf [3]) // sanitize the input a little...
|
||||
return 0;
|
||||
|
||||
inbufsize -= 4;
|
||||
inbuf += 4;
|
||||
}
|
||||
|
||||
chunks = inbufsize / (channels * 4);
|
||||
samples += chunks * 8;
|
||||
|
||||
while (chunks--) {
|
||||
int ch, i;
|
||||
|
||||
for (ch = 0; ch < channels; ++ch) {
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
int step = step_table [index [ch]], delta = step >> 3;
|
||||
|
||||
if (*inbuf & 1) delta += (step >> 2);
|
||||
if (*inbuf & 2) delta += (step >> 1);
|
||||
if (*inbuf & 4) delta += step;
|
||||
if (*inbuf & 8) delta = -delta;
|
||||
|
||||
pcmdata[ch] += delta;
|
||||
index[ch] += index_table [*inbuf & 0x7];
|
||||
CLIP(index[ch], 0, 88);
|
||||
CLIP(pcmdata[ch], -32768, 32767);
|
||||
outbuf [i * 2 * channels] = pcmdata[ch];
|
||||
|
||||
step = step_table [index [ch]], delta = step >> 3;
|
||||
|
||||
if (*inbuf & 0x10) delta += (step >> 2);
|
||||
if (*inbuf & 0x20) delta += (step >> 1);
|
||||
if (*inbuf & 0x40) delta += step;
|
||||
if (*inbuf & 0x80) delta = -delta;
|
||||
|
||||
pcmdata[ch] += delta;
|
||||
index[ch] += index_table [(*inbuf >> 4) & 0x7];
|
||||
CLIP(index[ch], 0, 88);
|
||||
CLIP(pcmdata[ch], -32768, 32767);
|
||||
outbuf [(i * 2 + 1) * channels] = pcmdata[ch];
|
||||
|
||||
inbuf++;
|
||||
}
|
||||
|
||||
outbuf++;
|
||||
}
|
||||
|
||||
outbuf += channels * 7;
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,9 @@
|
|||
/*
|
||||
* QEMU Geforce NV2A implementation
|
||||
* QEMU MCPX Audio Processing Unit implementation
|
||||
*
|
||||
* Copyright (c) 2018 Jannik Vogel
|
||||
* Copyright (c) 2012 espes
|
||||
* Copyright (c) 2018-2019 Jannik Vogel
|
||||
* Copyright (c) 2019-2021 Matt Borgerson
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* QEMU MCPX Audio Processing Unit implementation
|
||||
*
|
||||
* Copyright (c) 2020-2021 Matt Borgerson
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MCPX_APU_DEBUG_H
|
||||
#define MCPX_APU_DEBUG_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum McpxApuDebugMon {
|
||||
MCPX_APU_DEBUG_MON_AC97,
|
||||
MCPX_APU_DEBUG_MON_VP,
|
||||
MCPX_APU_DEBUG_MON_GP,
|
||||
MCPX_APU_DEBUG_MON_EP,
|
||||
MCPX_APU_DEBUG_MON_GP_OR_EP
|
||||
};
|
||||
|
||||
struct McpxApuDebugVoice
|
||||
{
|
||||
bool active;
|
||||
bool paused;
|
||||
bool stereo;
|
||||
uint8_t bin[8];
|
||||
uint16_t vol[8];
|
||||
|
||||
bool stream;
|
||||
bool loop;
|
||||
bool persist;
|
||||
bool multipass;
|
||||
bool linked;
|
||||
int container_size, sample_size;
|
||||
unsigned int samples_per_block;
|
||||
uint32_t ebo, cbo, lbo, ba;
|
||||
float rate;
|
||||
};
|
||||
|
||||
struct McpxApuDebugVp
|
||||
{
|
||||
struct McpxApuDebugVoice v[256];
|
||||
};
|
||||
|
||||
struct McpxApuDebugDsp
|
||||
{
|
||||
int cycles;
|
||||
};
|
||||
|
||||
struct McpxApuDebug
|
||||
{
|
||||
struct McpxApuDebugVp vp;
|
||||
struct McpxApuDebugDsp gp, ep;
|
||||
int frames_processed;
|
||||
float utilization;
|
||||
bool gp_realtime, ep_realtime;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
const struct McpxApuDebug *mcpx_apu_get_debug_info(void);
|
||||
int mcpx_apu_debug_get_monitor(void);
|
||||
void mcpx_apu_debug_set_monitor(int mon);
|
||||
void mcpx_apu_debug_isolate_voice(uint16_t v);
|
||||
void mcpx_apu_debug_clear_isolations(void);
|
||||
void mcpx_apu_debug_toggle_mute(uint16_t v);
|
||||
bool mcpx_apu_debug_is_muted(uint16_t v);
|
||||
void mcpx_apu_debug_set_gp_realtime_enabled(bool enable);
|
||||
void mcpx_apu_debug_set_ep_realtime_enabled(bool enable);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,346 @@
|
|||
/*
|
||||
* QEMU MCPX Audio Processing Unit implementation
|
||||
*
|
||||
* Copyright (c) 2012 espes
|
||||
* Copyright (c) 2018-2019 Jannik Vogel
|
||||
* Copyright (c) 2019-2021 Matt Borgerson
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MCPX_APU_REGS_H
|
||||
#define MCPX_APU_REGS_H
|
||||
|
||||
#define NV_PAPU_ISTS 0x00001000
|
||||
# define NV_PAPU_ISTS_GINTSTS (1 << 0)
|
||||
# define NV_PAPU_ISTS_FETINTSTS (1 << 4)
|
||||
# define NV_PAPU_ISTS_FENINTSTS (1 << 5)
|
||||
# define NV_PAPU_ISTS_FEVINTSTS (1 << 6)
|
||||
#define NV_PAPU_IEN 0x00001004
|
||||
#define NV_PAPU_FECTL 0x00001100
|
||||
# define NV_PAPU_FECTL_FEMETHMODE 0x000000E0
|
||||
# define NV_PAPU_FECTL_FEMETHMODE_FREE_RUNNING 0x00000000
|
||||
# define NV_PAPU_FECTL_FEMETHMODE_HALTED 0x00000080
|
||||
# define NV_PAPU_FECTL_FEMETHMODE_TRAPPED 0x000000E0
|
||||
# define NV_PAPU_FECTL_FETRAPREASON 0x00000F00
|
||||
# define NV_PAPU_FECTL_FETRAPREASON_REQUESTED 0x00000F00
|
||||
#define NV_PAPU_FECV 0x00001110
|
||||
#define NV_PAPU_FEAV 0x00001118
|
||||
# define NV_PAPU_FEAV_VALUE 0x0000FFFF
|
||||
# define NV_PAPU_FEAV_LST 0x00030000
|
||||
|
||||
#define NV_PAPU_FENADDR 0x0000115c
|
||||
|
||||
#define NV_PAPU_FEDECMETH 0x00001300
|
||||
#define NV_PAPU_FEDECPARAM 0x00001304
|
||||
#define NV_PAPU_FEMEMADDR 0x00001324
|
||||
#define NV_PAPU_FEMEMDATA 0x00001334
|
||||
#define NV_PAPU_FETFORCE0 0x00001500
|
||||
#define NV_PAPU_FETFORCE1 0x00001504
|
||||
# define NV_PAPU_FETFORCE1_SE2FE_IDLE_VOICE (1 << 15)
|
||||
#define NV_PAPU_SECTL 0x00002000
|
||||
# define NV_PAPU_SECTL_XCNTMODE 0x00000018
|
||||
# define NV_PAPU_SECTL_XCNTMODE_OFF 0
|
||||
#define NV_PAPU_XGSCNT 0x0000200C
|
||||
#define NV_PAPU_VPVADDR 0x0000202C
|
||||
#define NV_PAPU_VPSGEADDR 0x00002030
|
||||
#define NV_PAPU_VPSSLADDR 0x00002034
|
||||
#define NV_PAPU_GPSADDR 0x00002040
|
||||
#define NV_PAPU_GPFADDR 0x00002044
|
||||
#define NV_PAPU_EPSADDR 0x00002048
|
||||
#define NV_PAPU_EPFADDR 0x0000204C
|
||||
#define NV_PAPU_TVL2D 0x00002054
|
||||
#define NV_PAPU_CVL2D 0x00002058
|
||||
#define NV_PAPU_NVL2D 0x0000205C
|
||||
#define NV_PAPU_TVL3D 0x00002060
|
||||
#define NV_PAPU_CVL3D 0x00002064
|
||||
#define NV_PAPU_NVL3D 0x00002068
|
||||
#define NV_PAPU_TVLMP 0x0000206C
|
||||
#define NV_PAPU_CVLMP 0x00002070
|
||||
#define NV_PAPU_NVLMP 0x00002074
|
||||
#define NV_PAPU_GPSMAXSGE 0x000020D4
|
||||
#define NV_PAPU_GPFMAXSGE 0x000020D8
|
||||
#define NV_PAPU_EPSMAXSGE 0x000020DC
|
||||
#define NV_PAPU_EPFMAXSGE 0x000020E0
|
||||
|
||||
/* Each FIFO has the same fields */
|
||||
#define NV_PAPU_GPOFBASE0 0x00003024
|
||||
# define NV_PAPU_GPOFBASE0_VALUE 0x00FFFFFF // FIXME: Use ffff00 mask but shift appropriately
|
||||
#define NV_PAPU_GPOFEND0 0x00003028
|
||||
# define NV_PAPU_GPOFEND0_VALUE 0x00FFFFFF
|
||||
#define NV_PAPU_GPOFCUR0 0x0000302C
|
||||
# define NV_PAPU_GPOFCUR0_VALUE 0x00FFFFFF
|
||||
#define NV_PAPU_GPOFBASE1 0x00003034
|
||||
#define NV_PAPU_GPOFEND1 0x00003038
|
||||
#define NV_PAPU_GPOFCUR1 0x0000303C
|
||||
#define NV_PAPU_GPOFBASE2 0x00003044
|
||||
#define NV_PAPU_GPOFEND2 0x00003048
|
||||
#define NV_PAPU_GPOFCUR2 0x0000304C
|
||||
#define NV_PAPU_GPOFBASE3 0x00003054
|
||||
#define NV_PAPU_GPOFEND3 0x00003058
|
||||
#define NV_PAPU_GPOFCUR3 0x0000305C
|
||||
|
||||
/* Fields are same as for the 4 output FIFOs, but only 2 input FIFOs */
|
||||
#define NV_PAPU_GPIFBASE0 0x00003064
|
||||
#define NV_PAPU_GPIFEND0 0x00003068
|
||||
#define NV_PAPU_GPIFCUR0 0x0000306C
|
||||
#define NV_PAPU_GPIFBASE1 0x00003074
|
||||
#define NV_PAPU_GPIFEND1 0x00003078
|
||||
#define NV_PAPU_GPIFCUR1 0x0000307C
|
||||
|
||||
/* Fields, strides and count is same as for GP FIFOs */
|
||||
#define NV_PAPU_EPOFBASE0 0x00004024
|
||||
#define NV_PAPU_EPOFEND0 0x00004028
|
||||
#define NV_PAPU_EPOFCUR0 0x0000402C
|
||||
#define NV_PAPU_EPIFBASE0 0x00004064
|
||||
#define NV_PAPU_EPIFEND0 0x00004068
|
||||
#define NV_PAPU_EPIFCUR0 0x0000406C
|
||||
|
||||
#define NV_PAPU_GPXMEM 0x00000000
|
||||
#define NV_PAPU_GPMIXBUF 0x00005000
|
||||
#define NV_PAPU_GPYMEM 0x00006000
|
||||
#define NV_PAPU_GPPMEM 0x0000A000
|
||||
#define NV_PAPU_GPRST 0x0000FFFC
|
||||
#define NV_PAPU_GPRST_GPRST (1 << 0)
|
||||
#define NV_PAPU_GPRST_GPDSPRST (1 << 1)
|
||||
#define NV_PAPU_GPRST_GPNMI (1 << 2)
|
||||
#define NV_PAPU_GPRST_GPABORT (1 << 3)
|
||||
|
||||
#define NV_PAPU_EPXMEM 0x00000000
|
||||
#define NV_PAPU_EPYMEM 0x00006000
|
||||
#define NV_PAPU_EPPMEM 0x0000A000
|
||||
#define NV_PAPU_EPRST 0x0000FFFC
|
||||
|
||||
static const struct {
|
||||
hwaddr top, current, next;
|
||||
} voice_list_regs[] = {
|
||||
{NV_PAPU_TVL2D, NV_PAPU_CVL2D, NV_PAPU_NVL2D}, //2D
|
||||
{NV_PAPU_TVL3D, NV_PAPU_CVL3D, NV_PAPU_NVL3D}, //3D
|
||||
{NV_PAPU_TVLMP, NV_PAPU_CVLMP, NV_PAPU_NVLMP}, //MP
|
||||
};
|
||||
|
||||
|
||||
/* audio processor object / front-end messages */
|
||||
#define NV1BA0_PIO_FREE 0x00000010
|
||||
#define NV1BA0_PIO_SET_ANTECEDENT_VOICE 0x00000120
|
||||
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_HANDLE 0x0000FFFF
|
||||
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST 0x00030000
|
||||
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_INHERIT 0
|
||||
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_2D_TOP 1
|
||||
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_3D_TOP 2
|
||||
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_MP_TOP 3
|
||||
#define NV1BA0_PIO_VOICE_ON 0x00000124
|
||||
# define NV1BA0_PIO_VOICE_ON_HANDLE 0x0000FFFF
|
||||
# define NV1BA0_PIO_VOICE_ON_ENVF 0x0F000000
|
||||
# define NV1BA0_PIO_VOICE_ON_ENVA 0xF0000000
|
||||
#define NV1BA0_PIO_VOICE_OFF 0x00000128
|
||||
# define NV1BA0_PIO_VOICE_OFF_HANDLE 0x0000FFFF
|
||||
#define NV1BA0_PIO_VOICE_RELEASE 0x0000012C
|
||||
#define NV1BA0_PIO_GET_VOICE_POSITION 0x00000130
|
||||
# define NV1BA0_PIO_VOICE_RELEASE_HANDLE 0x0000FFFF
|
||||
#define NV1BA0_PIO_VOICE_PAUSE 0x00000140
|
||||
# define NV1BA0_PIO_VOICE_PAUSE_HANDLE 0x0000FFFF
|
||||
# define NV1BA0_PIO_VOICE_PAUSE_ACTION (1 << 18)
|
||||
#define NV1BA0_PIO_SET_CONTEXT_DMA_NOTIFY 0x00000180
|
||||
#define NV1BA0_PIO_SET_CURRENT_SSL_CONTEXT_DMA 0x0000018C
|
||||
#define NV1BA0_PIO_SET_CURRENT_SSL 0x00000190
|
||||
# define NV1BA0_PIO_SET_CURRENT_SSL_BASE_PAGE 0x3FFFC0
|
||||
#define NV1BA0_PIO_SET_SSL_SEGMENT_OFFSET 0x00000600
|
||||
#define NV1BA0_PIO_SET_SSL_SEGMENT_LENGTH 0x00000604
|
||||
#define NV1BA0_PIO_SET_SUBMIX_HEADROOM 0x00000200
|
||||
# define NV1BA0_PIO_SET_SUBMIX_HEADROOM_AMOUNT 0x7
|
||||
#define NV1BA0_PIO_SET_HRTF_HEADROOM 0x00000280
|
||||
# define NV1BA0_PIO_SET_HRTF_HEADROOM_AMOUNT 0x7
|
||||
#define NV1BA0_PIO_SET_HRTF_SUBMIXES 0x000002C0
|
||||
#define NV1BA0_PIO_SET_CURRENT_VOICE 0x000002F8
|
||||
#define NV1BA0_PIO_VOICE_LOCK 0x000002FC
|
||||
#define NV1BA0_PIO_SET_VOICE_CFG_VBIN 0x00000300
|
||||
#define NV1BA0_PIO_SET_VOICE_CFG_FMT 0x00000304
|
||||
#define NV1BA0_PIO_SET_VOICE_CFG_ENV0 0x00000308
|
||||
#define NV1BA0_PIO_SET_VOICE_CFG_ENVA 0x0000030C
|
||||
#define NV1BA0_PIO_SET_VOICE_CFG_ENV1 0x00000310
|
||||
#define NV1BA0_PIO_SET_VOICE_CFG_ENVF 0x00000314
|
||||
#define NV1BA0_PIO_SET_VOICE_CFG_MISC 0x00000318
|
||||
#define NV1BA0_PIO_SET_VOICE_SSL_A 0x00000320
|
||||
# define NV1BA0_PIO_SET_VOICE_SSL_A_COUNT 0x000000FF
|
||||
# define NV1BA0_PIO_SET_VOICE_SSL_A_BASE 0xFFFFFF00
|
||||
#define NV1BA0_PIO_SET_VOICE_SSL_B 0x0000035C
|
||||
#define NV1BA0_PIO_SET_VOICE_TAR_VOLA 0x00000360
|
||||
#define NV1BA0_PIO_SET_VOICE_TAR_VOLB 0x00000364
|
||||
#define NV1BA0_PIO_SET_VOICE_TAR_VOLC 0x00000368
|
||||
#define NV1BA0_PIO_SET_VOICE_LFO_ENV 0x0000036C
|
||||
#define NV1BA0_PIO_SET_VOICE_TAR_FCA 0x00000374
|
||||
#define NV1BA0_PIO_SET_VOICE_TAR_FCB 0x00000378
|
||||
#define NV1BA0_PIO_SET_VOICE_TAR_PITCH 0x0000037C
|
||||
# define NV1BA0_PIO_SET_VOICE_TAR_PITCH_STEP 0xFFFF0000
|
||||
#define NV1BA0_PIO_SET_VOICE_CFG_BUF_BASE 0x000003A0
|
||||
# define NV1BA0_PIO_SET_VOICE_CFG_BUF_BASE_OFFSET 0x00FFFFFF
|
||||
#define NV1BA0_PIO_SET_VOICE_CFG_BUF_LBO 0x000003A4
|
||||
# define NV1BA0_PIO_SET_VOICE_CFG_BUF_LBO_OFFSET 0x00FFFFFF
|
||||
#define NV1BA0_PIO_SET_VOICE_BUF_CBO 0x000003D8
|
||||
# define NV1BA0_PIO_SET_VOICE_BUF_CBO_OFFSET 0x00FFFFFF
|
||||
#define NV1BA0_PIO_SET_VOICE_CFG_BUF_EBO 0x000003DC
|
||||
# define NV1BA0_PIO_SET_VOICE_CFG_BUF_EBO_OFFSET 0x00FFFFFF
|
||||
#define NV1BA0_PIO_SET_SSL_SEGMENT_OFFSET 0x00000600
|
||||
#define NV1BA0_PIO_SET_SSL_SEGMENT_LENGTH 0x00000604
|
||||
#define NV1BA0_PIO_SET_CURRENT_INBUF_SGE 0x00000804
|
||||
# define NV1BA0_PIO_SET_CURRENT_INBUF_SGE_HANDLE 0xFFFFFFFF
|
||||
#define NV1BA0_PIO_SET_CURRENT_INBUF_SGE_OFFSET 0x00000808
|
||||
# define NV1BA0_PIO_SET_CURRENT_INBUF_SGE_OFFSET_PARAMETER 0xFFFFF000
|
||||
#define NV1BA0_PIO_SET_OUTBUF_BA 0x00001000 // 8 byte pitch, 4 entries
|
||||
# define NV1BA0_PIO_SET_OUTBUF_BA_ADDRESS 0x007FFF00
|
||||
#define NV1BA0_PIO_SET_OUTBUF_LEN 0x00001004 // 8 byte pitch, 4 entries
|
||||
# define NV1BA0_PIO_SET_OUTBUF_LEN_VALUE 0x007FFF00
|
||||
#define NV1BA0_PIO_SET_CURRENT_OUTBUF_SGE 0x00001800
|
||||
# define NV1BA0_PIO_SET_CURRENT_OUTBUF_SGE_HANDLE 0xFFFFFFFF
|
||||
#define NV1BA0_PIO_SET_CURRENT_OUTBUF_SGE_OFFSET 0x00001808
|
||||
# define NV1BA0_PIO_SET_CURRENT_OUTBUF_SGE_OFFSET_PARAMETER 0xFFFFF000
|
||||
|
||||
#define SE2FE_IDLE_VOICE 0x00008000
|
||||
|
||||
|
||||
/* voice structure */
|
||||
#define NV_PAVS_SIZE 0x00000080
|
||||
#define NV_PAVS_VOICE_CFG_VBIN 0x00000000
|
||||
# define NV_PAVS_VOICE_CFG_VBIN_V0BIN (0x1F << 0)
|
||||
# define NV_PAVS_VOICE_CFG_VBIN_V1BIN (0x1F << 5)
|
||||
# define NV_PAVS_VOICE_CFG_VBIN_V2BIN (0x1F << 10)
|
||||
# define NV_PAVS_VOICE_CFG_VBIN_V3BIN (0x1F << 16)
|
||||
# define NV_PAVS_VOICE_CFG_VBIN_V4BIN (0x1F << 21)
|
||||
# define NV_PAVS_VOICE_CFG_VBIN_V5BIN (0x1F << 26)
|
||||
#define NV_PAVS_VOICE_CFG_FMT 0x00000004
|
||||
# define NV_PAVS_VOICE_CFG_FMT_V6BIN (0x1F << 0)
|
||||
# define NV_PAVS_VOICE_CFG_FMT_V7BIN (0x1F << 5)
|
||||
# define NV_PAVS_VOICE_CFG_FMT_HEADROOM (0x7 << 13)
|
||||
# define NV_PAVS_VOICE_CFG_FMT_SAMPLES_PER_BLOCK (0x1F << 16)
|
||||
# define NV_PAVS_VOICE_CFG_FMT_MULTIPASS_BIN (0x1F << 16)
|
||||
# define NV_PAVS_VOICE_CFG_FMT_MULTIPASS (1 << 21)
|
||||
# define NV_PAVS_VOICE_CFG_FMT_LINKED (1 << 22)
|
||||
# define NV_PAVS_VOICE_CFG_FMT_PERSIST (1 << 23)
|
||||
# define NV_PAVS_VOICE_CFG_FMT_DATA_TYPE (1 << 24)
|
||||
# define NV_PAVS_VOICE_CFG_FMT_LOOP (1 << 25)
|
||||
# define NV_PAVS_VOICE_CFG_FMT_CLEAR_MIX (1 << 26)
|
||||
# define NV_PAVS_VOICE_CFG_FMT_STEREO (1 << 27)
|
||||
# define NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE (0x3 << 28)
|
||||
# define NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE_U8 0
|
||||
# define NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE_S16 1
|
||||
# define NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE_S24 2
|
||||
# define NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE_S32 3
|
||||
# define NV_PAVS_VOICE_CFG_FMT_CONTAINER_SIZE (0x3 << 30)
|
||||
# define NV_PAVS_VOICE_CFG_FMT_CONTAINER_SIZE_B8 0
|
||||
# define NV_PAVS_VOICE_CFG_FMT_CONTAINER_SIZE_B16 1
|
||||
# define NV_PAVS_VOICE_CFG_FMT_CONTAINER_SIZE_ADPCM 2
|
||||
# define NV_PAVS_VOICE_CFG_FMT_CONTAINER_SIZE_B32 3
|
||||
#define NV_PAVS_VOICE_CFG_ENV0 0x00000008
|
||||
# define NV_PAVS_VOICE_CFG_ENV0_EA_ATTACKRATE (0xFFF << 0)
|
||||
# define NV_PAVS_VOICE_CFG_ENV0_EA_DELAYTIME (0xFFF << 12)
|
||||
# define NV_PAVS_VOICE_CFG_ENV0_EF_PITCHSCALE (0xFF << 24)
|
||||
#define NV_PAVS_VOICE_CFG_ENVA 0x0000000C
|
||||
# define NV_PAVS_VOICE_CFG_ENVA_EA_DECAYRATE (0xFFF << 0)
|
||||
# define NV_PAVS_VOICE_CFG_ENVA_EA_HOLDTIME (0xFFF << 12)
|
||||
# define NV_PAVS_VOICE_CFG_ENVA_EA_SUSTAINLEVEL (0xFF << 24)
|
||||
#define NV_PAVS_VOICE_CFG_ENV1 0x00000010
|
||||
# define NV_PAVS_VOICE_CFG_ENV1_EF_FCSCALE (0xFF << 24)
|
||||
#define NV_PAVS_VOICE_CFG_ENVF 0x00000014
|
||||
#define NV_PAVS_VOICE_CFG_MISC 0x00000018
|
||||
# define NV_PAVS_VOICE_CFG_MISC_EF_RELEASERATE (0xFFF << 0)
|
||||
# define NV_PAVS_VOICE_CFG_MISC_FMODE (3 << 16)
|
||||
#define NV_PAVS_VOICE_CUR_PSL_START 0x00000020
|
||||
# define NV_PAVS_VOICE_CUR_PSL_START_BA 0x00FFFFFF
|
||||
#define NV_PAVS_VOICE_CUR_PSH_SAMPLE 0x00000024
|
||||
# define NV_PAVS_VOICE_CUR_PSH_SAMPLE_LBO 0x00FFFFFF
|
||||
|
||||
#define NV_PAVS_VOICE_CUR_ECNT 0x00000034
|
||||
# define NV_PAVS_VOICE_CUR_ECNT_EACOUNT 0x0000FFFF
|
||||
# define NV_PAVS_VOICE_CUR_ECNT_EFCOUNT 0xFFFF0000
|
||||
|
||||
#define NV_PAVS_VOICE_PAR_STATE 0x00000054
|
||||
# define NV_PAVS_VOICE_PAR_STATE_PAUSED (1 << 18)
|
||||
# define NV_PAVS_VOICE_PAR_STATE_NEW_VOICE (1 << 20)
|
||||
# define NV_PAVS_VOICE_PAR_STATE_ACTIVE_VOICE (1 << 21)
|
||||
# define NV_PAVS_VOICE_PAR_STATE_EFCUR (0xF << 24)
|
||||
# define NV_PAVS_VOICE_PAR_STATE_EFCUR_OFF 0
|
||||
# define NV_PAVS_VOICE_PAR_STATE_EFCUR_DELAY 1
|
||||
# define NV_PAVS_VOICE_PAR_STATE_EFCUR_ATTACK 2
|
||||
# define NV_PAVS_VOICE_PAR_STATE_EFCUR_HOLD 3
|
||||
# define NV_PAVS_VOICE_PAR_STATE_EFCUR_DECAY 4
|
||||
# define NV_PAVS_VOICE_PAR_STATE_EFCUR_SUSTAIN 5
|
||||
# define NV_PAVS_VOICE_PAR_STATE_EFCUR_RELEASE 6
|
||||
# define NV_PAVS_VOICE_PAR_STATE_EFCUR_FORCE_RELEASE 7
|
||||
# define NV_PAVS_VOICE_PAR_STATE_EACUR (0xF << 28)
|
||||
#define NV_PAVS_VOICE_PAR_OFFSET 0x00000058
|
||||
# define NV_PAVS_VOICE_PAR_OFFSET_CBO 0x00FFFFFF
|
||||
# define NV_PAVS_VOICE_PAR_OFFSET_EALVL 0xFF000000
|
||||
#define NV_PAVS_VOICE_PAR_NEXT 0x0000005C
|
||||
# define NV_PAVS_VOICE_PAR_NEXT_EBO 0x00FFFFFF
|
||||
# define NV_PAVS_VOICE_PAR_NEXT_EFLVL 0xFF000000
|
||||
#define NV_PAVS_VOICE_TAR_VOLA 0x00000060
|
||||
# define NV_PAVS_VOICE_TAR_VOLA_VOLUME6_B3_0 0x0000000F
|
||||
# define NV_PAVS_VOICE_TAR_VOLA_VOLUME0 0x0000FFF0
|
||||
# define NV_PAVS_VOICE_TAR_VOLA_VOLUME7_B3_0 0x000F0000
|
||||
# define NV_PAVS_VOICE_TAR_VOLA_VOLUME1 0xFFF00000
|
||||
#define NV_PAVS_VOICE_TAR_VOLB 0x00000064
|
||||
# define NV_PAVS_VOICE_TAR_VOLB_VOLUME6_B7_4 0x0000000F
|
||||
# define NV_PAVS_VOICE_TAR_VOLB_VOLUME2 0x0000FFF0
|
||||
# define NV_PAVS_VOICE_TAR_VOLB_VOLUME7_B7_4 0x000F0000
|
||||
# define NV_PAVS_VOICE_TAR_VOLB_VOLUME3 0xFFF00000
|
||||
#define NV_PAVS_VOICE_TAR_VOLC 0x00000068
|
||||
# define NV_PAVS_VOICE_TAR_VOLC_VOLUME6_B11_8 0x0000000F
|
||||
# define NV_PAVS_VOICE_TAR_VOLC_VOLUME4 0x0000FFF0
|
||||
# define NV_PAVS_VOICE_TAR_VOLC_VOLUME7_B11_8 0x000F0000
|
||||
# define NV_PAVS_VOICE_TAR_VOLC_VOLUME5 0xFFF00000
|
||||
#define NV_PAVS_VOICE_TAR_LFO_ENV 0x0000006C
|
||||
# define NV_PAVS_VOICE_TAR_LFO_ENV_EA_RELEASERATE (0xFFF << 0)
|
||||
#define NV_PAVS_VOICE_TAR_FCA 0x00000074
|
||||
# define NV_PAVS_VOICE_TAR_FCA_FC0 0x0000FFFF
|
||||
# define NV_PAVS_VOICE_TAR_FCA_FC1 0xFFFF0000
|
||||
#define NV_PAVS_VOICE_TAR_FCB 0x00000078
|
||||
#define NV_PAVS_VOICE_TAR_PITCH_LINK 0x0000007C
|
||||
# define NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE 0x0000FFFF
|
||||
# define NV_PAVS_VOICE_TAR_PITCH_LINK_PITCH 0xFFFF0000
|
||||
|
||||
|
||||
#define GP_DSP_MIXBUF_BASE 0x001400
|
||||
|
||||
#define GP_OUTPUT_FIFO_COUNT 4
|
||||
#define GP_INPUT_FIFO_COUNT 2
|
||||
|
||||
#define EP_OUTPUT_FIFO_COUNT 4
|
||||
#define EP_INPUT_FIFO_COUNT 2
|
||||
|
||||
#define MCPX_HW_MAX_VOICES 256
|
||||
|
||||
#define NUM_SAMPLES_PER_FRAME 32
|
||||
#define NUM_MIXBINS 32
|
||||
|
||||
#define ADPCM_SAMPLES_PER_BLOCK 64 // FIXME: Should be 65? Check interpolation
|
||||
|
||||
#define MCPX_HW_MAX_PRD_ENTRIES_PER_SSL 16
|
||||
#define MCPX_HW_SSLS_PER_VOICE 2
|
||||
#define MCPX_HW_MAX_PRD_ENTRIES_PER_VOICE (MCPX_HW_MAX_PRD_ENTRIES_PER_SSL * MCPX_HW_SSLS_PER_VOICE)
|
||||
#define MCPX_HW_MAX_SSL_PRDS (MCPX_HW_MAX_VOICES * MCPX_HW_MAX_PRD_ENTRIES_PER_VOICE)
|
||||
|
||||
#define NV_PSGE_SIZE 0x00000008
|
||||
|
||||
#define MCPX_HW_NOTIFIER_BASE_OFFSET 2
|
||||
enum MCPX_HW_NOTIFIER {
|
||||
MCPX_HW_NOTIFIER_SSLA_DONE = 0,
|
||||
MCPX_HW_NOTIFIER_SSLB_DONE,
|
||||
// ...
|
||||
MCPX_HW_NOTIFIER_COUNT = 4,
|
||||
};
|
||||
#define NV1BA0_NOTIFICATION_STATUS_DONE_SUCCESS 0x01
|
||||
#define NV1BA0_NOTIFICATION_STATUS_IN_PROGRESS 0x80
|
||||
|
||||
#endif
|
|
@ -44,7 +44,15 @@
|
|||
#define INTERRUPT_START_FRAME (1 << 1)
|
||||
#define INTERRUPT_DMA_EOL (1 << 7)
|
||||
|
||||
#define DPRINTF(s, ...) printf(s, ## __VA_ARGS__)
|
||||
// #define DEBUG_DSP
|
||||
|
||||
#ifdef DEBUG_DSP
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
|
||||
#else
|
||||
#define DPRINTF(fmt, ...) \
|
||||
do { } while (0)
|
||||
#endif
|
||||
|
||||
static uint32_t read_peripheral(dsp_core_t* core, uint32_t address);
|
||||
static void write_peripheral(dsp_core_t* core, uint32_t address, uint32_t value);
|
||||
|
@ -85,10 +93,13 @@ void dsp_destroy(DSPState* dsp)
|
|||
static uint32_t read_peripheral(dsp_core_t* core, uint32_t address) {
|
||||
DSPState* dsp = container_of(core, DSPState, core);
|
||||
|
||||
// printf("read_peripheral 0x%06x\n", address);
|
||||
DPRINTF("read_peripheral 0x%06x", address);
|
||||
|
||||
uint32_t v = 0xababa;
|
||||
switch(address) {
|
||||
case 0xFFFFB3:
|
||||
v = 0; // core->num_inst; // ??
|
||||
break;
|
||||
case 0xFFFFC5:
|
||||
v = dsp->interrupts;
|
||||
if (dsp->dma.eol) {
|
||||
|
@ -109,16 +120,22 @@ static uint32_t read_peripheral(dsp_core_t* core, uint32_t address) {
|
|||
break;
|
||||
}
|
||||
|
||||
// printf(" -> 0x%06x\n", v);
|
||||
DPRINTF(" -> 0x%06x\n", v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static void write_peripheral(dsp_core_t* core, uint32_t address, uint32_t value) {
|
||||
DSPState* dsp = container_of(core, DSPState, core);
|
||||
|
||||
// printf("write_peripheral [0x%06x] = 0x%06x\n", address, value);
|
||||
DPRINTF("write_peripheral [0x%06x] = 0x%06x\n", address, value);
|
||||
|
||||
switch(address) {
|
||||
case 0xFFFFC4:
|
||||
if (value & 1) {
|
||||
core->is_idle = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xFFFFC5:
|
||||
dsp->interrupts &= ~value;
|
||||
if (value & INTERRUPT_DMA_EOL) {
|
||||
|
@ -137,6 +154,8 @@ static void write_peripheral(dsp_core_t* core, uint32_t address, uint32_t value)
|
|||
case 0xFFFFD7:
|
||||
dsp_dma_write(&dsp->dma, DMA_CONFIGURATION, value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,28 +171,48 @@ void dsp_run(DSPState* dsp, int cycles)
|
|||
|
||||
if (dsp->save_cycles <= 0) return;
|
||||
|
||||
// if (unlikely(bDspDebugging)) {
|
||||
// while (dsp->core.save_cycles > 0)
|
||||
// {
|
||||
// dsp56k_execute_instruction();
|
||||
// dsp->core.save_cycles -= dsp->core.instr_cycle;
|
||||
// DebugDsp_Check();
|
||||
// }
|
||||
// } else {
|
||||
// printf("--> %d\n", dsp->core.save_cycles);
|
||||
int count = 0;
|
||||
int dma_timer = 0;
|
||||
|
||||
while (dsp->save_cycles > 0)
|
||||
{
|
||||
dsp56k_execute_instruction(&dsp->core);
|
||||
dsp->save_cycles -= dsp->core.instr_cycle;
|
||||
dsp->core.cycle_count++;
|
||||
count++;
|
||||
|
||||
if (dsp->dma.control & DMA_CONTROL_RUNNING) {
|
||||
dma_timer++;
|
||||
}
|
||||
|
||||
if (dma_timer > 2) {
|
||||
dma_timer = 0;
|
||||
dsp->dma.control &= ~DMA_CONTROL_RUNNING;
|
||||
dsp->dma.control |= DMA_CONTROL_STOPPED;
|
||||
}
|
||||
|
||||
if (dsp->core.is_idle) break;
|
||||
}
|
||||
|
||||
}
|
||||
/* FIXME: DMA timing be done cleaner. Xbox enables running
|
||||
* then polls to make sure its running. But we complete DMA instantaneously,
|
||||
* so when is it supposed to be signaled that it stopped? Maybe just wait at
|
||||
* least one cycle? How long does hardware wait?
|
||||
*/
|
||||
}
|
||||
|
||||
void dsp_bootstrap(DSPState* dsp)
|
||||
{
|
||||
// scratch memory is dma'd in to pram by the bootrom
|
||||
dsp->dma.scratch_rw(dsp->dma.rw_opaque,
|
||||
(uint8_t*)dsp->core.pram, 0, 0x800*4, false);
|
||||
for (int i = 0; i < 0x800; i++) {
|
||||
if (dsp->core.pram[i] & 0xff000000) {
|
||||
DPRINTF(stderr, "Bootstrap %04x: %08x\n", i, dsp->core.pram[i]);
|
||||
dsp->core.pram[i] &= 0x00ffffff;
|
||||
}
|
||||
}
|
||||
memset(dsp->core.pram_opcache, 0, sizeof(dsp->core.pram_opcache));
|
||||
}
|
||||
|
||||
void dsp_start_frame(DSPState* dsp)
|
||||
|
@ -294,12 +333,12 @@ void dsp_print_registers(DSPState* dsp)
|
|||
dsp->core.registers[DSP_REG_A2], dsp->core.registers[DSP_REG_A1], dsp->core.registers[DSP_REG_A0]);
|
||||
printf("B: B2: %02x B1: %06x B0: %06x\n",
|
||||
dsp->core.registers[DSP_REG_B2], dsp->core.registers[DSP_REG_B1], dsp->core.registers[DSP_REG_B0]);
|
||||
|
||||
|
||||
printf("X: X1: %06x X0: %06x\n", dsp->core.registers[DSP_REG_X1], dsp->core.registers[DSP_REG_X0]);
|
||||
printf("Y: Y1: %06x Y0: %06x\n", dsp->core.registers[DSP_REG_Y1], dsp->core.registers[DSP_REG_Y0]);
|
||||
|
||||
for (i=0; i<8; i++) {
|
||||
printf("R%01x: %04x N%01x: %04x M%01x: %04x\n",
|
||||
printf("R%01x: %04x N%01x: %04x M%01x: %04x\n",
|
||||
i, dsp->core.registers[DSP_REG_R0+i],
|
||||
i, dsp->core.registers[DSP_REG_N0+i],
|
||||
i, dsp->core.registers[DSP_REG_M0+i]);
|
||||
|
@ -307,7 +346,7 @@ void dsp_print_registers(DSPState* dsp)
|
|||
|
||||
printf("LA: %04x LC: %04x PC: %04x\n", dsp->core.registers[DSP_REG_LA], dsp->core.registers[DSP_REG_LC], dsp->core.pc);
|
||||
printf("SR: %04x OMR: %02x\n", dsp->core.registers[DSP_REG_SR], dsp->core.registers[DSP_REG_OMR]);
|
||||
printf("SP: %02x SSH: %04x SSL: %04x\n",
|
||||
printf("SP: %02x SSH: %04x SSL: %04x\n",
|
||||
dsp->core.registers[DSP_REG_SP], dsp->core.registers[DSP_REG_SSH], dsp->core.registers[DSP_REG_SSL]);
|
||||
}
|
||||
|
||||
|
@ -328,7 +367,7 @@ int dsp_get_register_address(DSPState* dsp, const char *regname, uint32_t **addr
|
|||
size_t bits;
|
||||
uint32_t mask;
|
||||
} reg_addr_t;
|
||||
|
||||
|
||||
/* sorted by name so that this can be bisected */
|
||||
const reg_addr_t registers[] = {
|
||||
|
||||
|
@ -409,7 +448,7 @@ int dsp_get_register_address(DSPState* dsp, const char *regname, uint32_t **addr
|
|||
return 0;
|
||||
}
|
||||
len = i;
|
||||
|
||||
|
||||
/* bisect */
|
||||
l = 0;
|
||||
r = ARRAYSIZE(registers) - 1;
|
||||
|
@ -449,7 +488,7 @@ bool dsp_disasm_set_register(DSPState* dsp, const char *arg, uint32_t value)
|
|||
if (arg[0]=='S' || arg[0]=='s') {
|
||||
if (arg[1]=='P' || arg[1]=='p') {
|
||||
dsp->core.registers[DSP_REG_SP] = value & BITMASK(6);
|
||||
value &= BITMASK(4);
|
||||
value &= BITMASK(4);
|
||||
dsp->core.registers[DSP_REG_SSH] = dsp->core.stack[0][value];
|
||||
dsp->core.registers[DSP_REG_SSL] = dsp->core.stack[1][value];
|
||||
return true;
|
|
@ -30,6 +30,7 @@
|
|||
#include "qemu/bswap.h"
|
||||
#include "dsp_cpu.h"
|
||||
|
||||
|
||||
#define TRACE_DSP_DISASM 0
|
||||
#define TRACE_DSP_DISASM_REG 0
|
||||
#define TRACE_DSP_DISASM_MEM 0
|
||||
|
@ -62,7 +63,7 @@ static uint32_t read_memory_disasm(dsp_core_t* dsp, int space, uint32_t address)
|
|||
static void write_memory_raw(dsp_core_t* dsp, int space, uint32_t address, uint32_t value);
|
||||
static void write_memory_disasm(dsp_core_t* dsp, int space, uint32_t address, uint32_t value);
|
||||
|
||||
static void dsp_write_reg(dsp_core_t* dsp, uint32_t numreg, uint32_t value);
|
||||
static void dsp_write_reg(dsp_core_t* dsp, uint32_t numreg, uint32_t value);
|
||||
|
||||
static void dsp_stack_push(dsp_core_t* dsp, uint32_t curpc, uint32_t cursr, uint16_t sshOnly);
|
||||
static void dsp_stack_pop(dsp_core_t* dsp, uint32_t *curpc, uint32_t *cursr);
|
||||
|
@ -120,12 +121,12 @@ static const int registers_mask[64] = {
|
|||
24, 24, 24, 24,
|
||||
24, 24, 8, 8,
|
||||
24, 24, 24, 24,
|
||||
|
||||
|
||||
16, 16, 16, 16,
|
||||
16, 16, 16, 16,
|
||||
16, 16, 16, 16,
|
||||
16, 16, 16, 16,
|
||||
|
||||
|
||||
16, 16, 16, 16,
|
||||
16, 16, 16, 16,
|
||||
0, 0, 0, 0,
|
||||
|
@ -284,7 +285,7 @@ static const OpcodeEntry nonparallel_opcodes[] = {
|
|||
{ "0000101111DDDDDD001bbbbb", "jsset #n, S, xxxx", dis_jsset_reg, emu_jsset_reg },
|
||||
{ "0000010011000RRR000ddddd", "lra Rn, D", NULL, NULL },
|
||||
{ "0000010001000000010ddddd", "lra xxxx, D", NULL, NULL },
|
||||
{ "000011000001111010iiiiiD", "lsl #ii, D", NULL, NULL },
|
||||
{ "000011000001111010iiiiiD", "lsl #ii, D", dis_lsl_imm, emu_lsl_imm },
|
||||
{ "00001100000111100001sssD", "lsl S, D", NULL, NULL },
|
||||
{ "000011000001111011iiiiiD", "lsr #ii, D", NULL, NULL },
|
||||
{ "00001100000111100011sssD", "lsr S, D", NULL, NULL },
|
||||
|
@ -386,7 +387,7 @@ void dsp56k_reset_cpu(dsp_core_t* dsp)
|
|||
memset(dsp->periph, 0, sizeof(dsp->periph));
|
||||
memset(dsp->stack, 0, sizeof(dsp->stack));
|
||||
memset(dsp->registers, 0, sizeof(dsp->registers));
|
||||
|
||||
|
||||
/* Registers */
|
||||
dsp->pc = 0x0000;
|
||||
dsp->registers[DSP_REG_OMR]=0x02;
|
||||
|
@ -422,21 +423,37 @@ void dsp56k_reset_cpu(dsp_core_t* dsp)
|
|||
dsp->disasm_prev_inst_pc = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
static OpcodeEntry lookup_opcode(uint32_t op) {
|
||||
OpcodeEntry r = {0};
|
||||
int i;
|
||||
for (i=0; i<ARRAYSIZE(nonparallel_opcodes); i++) {
|
||||
static const OpcodeEntry *lookup_opcode_slow(uint32_t op) {
|
||||
for (int i = 0; i < ARRAYSIZE(nonparallel_opcodes); i++) {
|
||||
if ((op & nonparallel_matches[i][0]) == nonparallel_matches[i][1]) {
|
||||
if (nonparallel_opcodes[i].match_func
|
||||
if (nonparallel_opcodes[i].match_func
|
||||
&& !nonparallel_opcodes[i].match_func(op)) continue;
|
||||
if (r.template != NULL) {
|
||||
printf("qqq %x %s\n", op, r.template);
|
||||
}
|
||||
assert(r.template == NULL);
|
||||
r = nonparallel_opcodes[i];
|
||||
return &nonparallel_opcodes[i];
|
||||
}
|
||||
}
|
||||
return r;
|
||||
|
||||
fprintf(stderr, "op = %08x\n", op);
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const OpcodeEntry *lookup_opcode(uint32_t op) {
|
||||
static struct opcache_entry {
|
||||
uint32_t op;
|
||||
const OpcodeEntry *entry;
|
||||
} opcache[256];
|
||||
|
||||
uint8_t tag =
|
||||
((op >> 24) & 0xff) ^
|
||||
((op >> 16) & 0xff) ^
|
||||
((op >> 8) & 0xff) ^
|
||||
((op >> 0) & 0xff);
|
||||
if (opcache[tag].op != op || opcache[tag].entry == NULL) {
|
||||
opcache[tag].op = op;
|
||||
opcache[tag].entry = lookup_opcode_slow(op);
|
||||
}
|
||||
|
||||
return opcache[tag].entry;
|
||||
}
|
||||
|
||||
static uint16_t disasm_instruction(dsp_core_t* dsp, dsp_trace_disasm_t mode)
|
||||
|
@ -461,12 +478,12 @@ static uint16_t disasm_instruction(dsp_core_t* dsp, dsp_trace_disasm_t mode)
|
|||
dsp->disasm_parallelmove_name[0] = 0;
|
||||
|
||||
if (dsp->disasm_cur_inst < 0x100000) {
|
||||
const OpcodeEntry op = lookup_opcode(dsp->disasm_cur_inst);
|
||||
if (op.template) {
|
||||
if (op.dis_func) {
|
||||
op.dis_func(dsp);
|
||||
const OpcodeEntry *op = lookup_opcode(dsp->disasm_cur_inst);
|
||||
if (op->template) {
|
||||
if (op->dis_func) {
|
||||
op->dis_func(dsp);
|
||||
} else {
|
||||
sprintf(dsp->disasm_str_instr, "%s", op.name);
|
||||
sprintf(dsp->disasm_str_instr, "%s", op->name);
|
||||
}
|
||||
} else {
|
||||
dis_undefined(dsp);
|
||||
|
@ -492,7 +509,7 @@ static void disasm_reg_compare(dsp_core_t* dsp)
|
|||
int i;
|
||||
bool bRegA = false;
|
||||
bool bRegB = false;
|
||||
|
||||
|
||||
for (i=4; i<64; i++) {
|
||||
if (dsp->disasm_registers_save[i] == dsp->registers[i]) {
|
||||
continue;
|
||||
|
@ -625,7 +642,7 @@ uint16_t dsp56k_execute_one_disasm_instruction(dsp_core_t* dsp, FILE *out, uint3
|
|||
|
||||
/* Restore DSP context after executing instruction */
|
||||
memcpy(dsp, &dsp_core_save, sizeof(dsp_core_t));
|
||||
|
||||
|
||||
/* Unset DSP in disasm mode */
|
||||
dsp->executing_for_disasm = false;
|
||||
|
||||
|
@ -639,17 +656,17 @@ void dsp56k_execute_instruction(dsp_core_t* dsp)
|
|||
|
||||
/* Decode and execute current instruction */
|
||||
dsp->cur_inst = read_memory_p(dsp, dsp->pc);
|
||||
|
||||
|
||||
/* Initialize instruction size and cycle counter */
|
||||
dsp->cur_inst_len = 1;
|
||||
dsp->instr_cycle = 2;
|
||||
|
||||
/* Disasm current instruction ? (trace mode only) */
|
||||
if (TRACE_DSP_DISASM) {
|
||||
if (TRACE_DSP_DISASM) {
|
||||
/* Call disasm_instruction only when DSP is called in trace mode */
|
||||
if (!dsp->executing_for_disasm) {
|
||||
disasm_return = disasm_instruction(dsp, DSP_TRACE_MODE);
|
||||
|
||||
|
||||
if (disasm_return) {
|
||||
printf( "%s", disasm_get_instruction_text(dsp));
|
||||
}
|
||||
|
@ -659,13 +676,17 @@ void dsp56k_execute_instruction(dsp_core_t* dsp)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (dsp->cur_inst < 0x100000) {
|
||||
const OpcodeEntry op = lookup_opcode(dsp->cur_inst);
|
||||
if (op.emu_func) {
|
||||
op.emu_func(dsp);
|
||||
const OpcodeEntry *op = dsp->pram_opcache[dsp->pc];
|
||||
if (op == NULL) {
|
||||
op = lookup_opcode(dsp->cur_inst);
|
||||
dsp->pram_opcache[dsp->pc] = op;
|
||||
}
|
||||
if (op->emu_func) {
|
||||
op->emu_func(dsp);
|
||||
} else {
|
||||
printf("%x - %s\n", dsp->cur_inst, op.name);
|
||||
printf("%x - %s\n", dsp->cur_inst, op->name);
|
||||
emu_undefined(dsp);
|
||||
}
|
||||
} else {
|
||||
|
@ -704,6 +725,9 @@ void dsp56k_execute_instruction(dsp_core_t* dsp)
|
|||
/* Process Interrupts */
|
||||
dsp_postexecute_interrupts(dsp);
|
||||
|
||||
|
||||
dsp->num_inst += dsp->instr_cycle;
|
||||
|
||||
#ifdef DSP_COUNT_IPS
|
||||
++dsp->num_inst;
|
||||
if ((dsp->num_inst & 63) == 0) {
|
||||
|
@ -726,7 +750,7 @@ static void dsp_postexecute_update_pc(dsp_core_t* dsp)
|
|||
{
|
||||
/* When running a REP, PC must stay on the current instruction */
|
||||
if (dsp->loop_rep) {
|
||||
/* Is PC on the instruction to repeat ? */
|
||||
/* Is PC on the instruction to repeat ? */
|
||||
if (dsp->pc_on_rep==0) {
|
||||
--dsp->registers[DSP_REG_LC];
|
||||
dsp->registers[DSP_REG_LC] &= BITMASK(16);
|
||||
|
@ -754,7 +778,7 @@ static void dsp_postexecute_update_pc(dsp_core_t* dsp)
|
|||
if (dsp->registers[DSP_REG_SR] & (1<<DSP_SR_LF)) {
|
||||
|
||||
/* Did we execute the last instruction in loop ? */
|
||||
if (dsp->pc == dsp->registers[DSP_REG_LA] + 1) {
|
||||
if (dsp->pc == dsp->registers[DSP_REG_LA] + 1) {
|
||||
--dsp->registers[DSP_REG_LC];
|
||||
dsp->registers[DSP_REG_LC] &= BITMASK(16);
|
||||
|
||||
|
@ -786,7 +810,7 @@ void dsp56k_add_interrupt(dsp_core_t* dsp, uint16_t inter)
|
|||
return;
|
||||
|
||||
/* add this interrupt to the pending interrupts table */
|
||||
if (dsp->interrupt_is_pending[inter] == 0) {
|
||||
if (dsp->interrupt_is_pending[inter] == 0) {
|
||||
dsp->interrupt_is_pending[inter] = 1;
|
||||
dsp->interrupt_counter ++;
|
||||
}
|
||||
|
@ -818,7 +842,7 @@ static void dsp_postexecute_interrupts(dsp_core_t* dsp)
|
|||
instr = read_memory_p(dsp, dsp->interrupt_instr_fetch);
|
||||
if ( ((instr & 0xfff000) == 0x0d0000) || ((instr & 0xffc0ff) == 0x0bc080) ) {
|
||||
dsp->interrupt_state = DSP_INTERRUPT_LONG;
|
||||
dsp_stack_push(dsp, dsp->interrupt_save_pc, dsp->registers[DSP_REG_SR], 0);
|
||||
dsp_stack_push(dsp, dsp->interrupt_save_pc, dsp->registers[DSP_REG_SR], 0);
|
||||
dsp->registers[DSP_REG_SR] &= BITMASK(16)-((1<<DSP_SR_LF)|(1<<DSP_SR_T) |
|
||||
(1<<DSP_SR_S1)|(1<<DSP_SR_S0) |
|
||||
(1<<DSP_SR_I0)|(1<<DSP_SR_I1));
|
||||
|
@ -832,7 +856,7 @@ static void dsp_postexecute_interrupts(dsp_core_t* dsp)
|
|||
instr = read_memory_p(dsp, dsp->pc);
|
||||
if ( ((instr & 0xfff000) == 0x0d0000) || ((instr & 0xffc0ff) == 0x0bc080) ) {
|
||||
dsp->interrupt_state = DSP_INTERRUPT_LONG;
|
||||
dsp_stack_push(dsp, dsp->interrupt_save_pc, dsp->registers[DSP_REG_SR], 0);
|
||||
dsp_stack_push(dsp, dsp->interrupt_save_pc, dsp->registers[DSP_REG_SR], 0);
|
||||
dsp->registers[DSP_REG_SR] &= BITMASK(16)-((1<<DSP_SR_LF)|(1<<DSP_SR_T) |
|
||||
(1<<DSP_SR_S1)|(1<<DSP_SR_S0) |
|
||||
(1<<DSP_SR_I0)|(1<<DSP_SR_I1));
|
||||
|
@ -943,10 +967,10 @@ static void dsp_postexecute_interrupts(dsp_core_t* dsp)
|
|||
else if (dsp->interrupt_instr_fetch == 0xff) {
|
||||
/* Clear HC and HCP interrupt */
|
||||
// dsp->periph[DSP_SPACE_X][DSP_HOST_HSR] &= 0xff - (1<<DSP_HOST_HSR_HCP);
|
||||
// dsp->hostport[CPU_HOST_CVR] &= 0xff - (1<<CPU_HOST_CVR_HC);
|
||||
// dsp->hostport[CPU_HOST_CVR] &= 0xff - (1<<CPU_HOST_CVR_HC);
|
||||
|
||||
// dsp->interrupt_instr_fetch = dsp->hostport[CPU_HOST_CVR] & BITMASK(5);
|
||||
// dsp->interrupt_instr_fetch *= 2;
|
||||
// dsp->interrupt_instr_fetch *= 2;
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
@ -974,9 +998,15 @@ uint32_t dsp56k_read_memory(dsp_core_t* dsp, int space, uint32_t address)
|
|||
return dsp->read_peripheral(dsp, address);
|
||||
} else if (address >= DSP_MIXBUFFER_BASE && address < DSP_MIXBUFFER_BASE+DSP_MIXBUFFER_SIZE) {
|
||||
return dsp->mixbuffer[address-DSP_MIXBUFFER_BASE];
|
||||
} else if (address >= 0xc00 && address < 0xc00+DSP_MIXBUFFER_SIZE) {
|
||||
return dsp->mixbuffer[address-0xc00];
|
||||
} else {
|
||||
assert(address < DSP_XRAM_SIZE);
|
||||
return dsp->xram[address];
|
||||
if (address < DSP_XRAM_SIZE) {
|
||||
return dsp->xram[address];
|
||||
} else {
|
||||
fprintf(stderr, "Out of bounds read at %x!\n", address);
|
||||
return 0x00FFFFFF; // FIXME: What does the DSP actually do in this case?
|
||||
}
|
||||
}
|
||||
} else if (space == DSP_SPACE_Y) {
|
||||
assert(address < DSP_YRAM_SIZE);
|
||||
|
@ -996,7 +1026,7 @@ void dsp56k_write_memory(dsp_core_t* dsp, int space, uint32_t address, uint32_t
|
|||
|
||||
if (TRACE_DSP_DISASM_MEM)
|
||||
write_memory_disasm(dsp, space, address, value);
|
||||
else
|
||||
else
|
||||
write_memory_raw(dsp, space, address, value);
|
||||
}
|
||||
|
||||
|
@ -1012,6 +1042,8 @@ static void write_memory_raw(dsp_core_t* dsp, int space, uint32_t address, uint3
|
|||
return;
|
||||
} else if (address >= DSP_MIXBUFFER_BASE && address < DSP_MIXBUFFER_BASE+DSP_MIXBUFFER_SIZE) {
|
||||
dsp->mixbuffer[address-DSP_MIXBUFFER_BASE] = value;
|
||||
} else if (address >= 0xc00 && address < 0xc00+DSP_MIXBUFFER_SIZE) {
|
||||
dsp->mixbuffer[address-0xc00] = value;
|
||||
} else {
|
||||
assert(address < DSP_XRAM_SIZE);
|
||||
dsp->xram[address] = value;
|
||||
|
@ -1022,6 +1054,7 @@ static void write_memory_raw(dsp_core_t* dsp, int space, uint32_t address, uint3
|
|||
} else if (space == DSP_SPACE_P) {
|
||||
assert(address < DSP_PRAM_SIZE);
|
||||
stl_le_p(&dsp->pram[address], value);
|
||||
dsp->pram_opcache[address] = NULL;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
@ -1097,7 +1130,7 @@ static void dsp_write_reg(dsp_core_t* dsp, uint32_t numreg, uint32_t value)
|
|||
}
|
||||
} else {
|
||||
dsp->registers[DSP_REG_SP] = value & BITMASK(6);
|
||||
}
|
||||
}
|
||||
dsp_compute_ssh_ssl(dsp);
|
||||
break;
|
||||
case DSP_REG_SSH:
|
||||
|
@ -1112,7 +1145,7 @@ static void dsp_write_reg(dsp_core_t* dsp, uint32_t numreg, uint32_t value)
|
|||
dsp->registers[DSP_REG_SSL] = value & BITMASK(16);
|
||||
break;
|
||||
default:
|
||||
dsp->registers[numreg] = value;
|
||||
dsp->registers[numreg] = value;
|
||||
dsp->registers[numreg] &= BITMASK(registers_mask[numreg]);
|
||||
break;
|
||||
}
|
||||
|
@ -1139,7 +1172,7 @@ static void dsp_stack_push(dsp_core_t* dsp, uint32_t curpc, uint32_t cursr, uint
|
|||
if (dsp->exception_debugging)
|
||||
assert(false);
|
||||
}
|
||||
|
||||
|
||||
dsp->registers[DSP_REG_SP] = (underflow | stack_error | stack) & BITMASK(6);
|
||||
stack &= BITMASK(4);
|
||||
|
||||
|
@ -1254,7 +1287,7 @@ static uint16_t dsp_asr56(uint32_t *dest, int n)
|
|||
uint64_t dest_v = dest[2] | ((uint64_t)dest[1] << 24) | ((uint64_t)dest[0] << 48);
|
||||
|
||||
uint16_t carry = (dest_v >> (n-1)) & 1;
|
||||
|
||||
|
||||
dest_v >>= n;
|
||||
dest[2] = dest_v & BITMASK(24);
|
||||
dest[1] = (dest_v >> 24) & BITMASK(24);
|
||||
|
@ -1401,7 +1434,7 @@ static void dsp_rnd56(dsp_core_t* dsp, uint32_t *dest)
|
|||
rnd_const[1] = 0;
|
||||
rnd_const[2] = (1<<22);
|
||||
dsp_add56(rnd_const, dest);
|
||||
|
||||
|
||||
if ((dest[2] & 0x7fffff) == 0){
|
||||
dest[2] = 0;
|
||||
}
|
|
@ -154,6 +154,10 @@ typedef struct dsp_interrupt_s {
|
|||
typedef struct dsp_core_s dsp_core_t;
|
||||
|
||||
struct dsp_core_s {
|
||||
bool is_gp;
|
||||
bool is_idle;
|
||||
uint32_t cycle_count;
|
||||
|
||||
/* DSP instruction Cycle counter */
|
||||
uint16_t instr_cycle;
|
||||
|
||||
|
@ -167,6 +171,7 @@ struct dsp_core_s {
|
|||
uint32_t xram[DSP_XRAM_SIZE];
|
||||
uint32_t yram[DSP_YRAM_SIZE];
|
||||
uint32_t pram[DSP_PRAM_SIZE];
|
||||
const void *pram_opcache[DSP_PRAM_SIZE];
|
||||
|
||||
uint32_t mixbuffer[DSP_MIXBUFFER_SIZE];
|
||||
|
||||
|
@ -227,8 +232,8 @@ struct dsp_core_s {
|
|||
uint16_t disasm_cur_inst_len;
|
||||
|
||||
/* Current instruction */
|
||||
char disasm_str_instr[128];
|
||||
char disasm_str_instr2[128];
|
||||
char disasm_str_instr[256];
|
||||
char disasm_str_instr2[523];
|
||||
char disasm_parallelmove_name[64];
|
||||
|
||||
/**********************************
|
|
@ -301,6 +301,15 @@ static void dis_asl_imm(dsp_core_t* dsp)
|
|||
registers_name[D ? DSP_REG_B : DSP_REG_A]);
|
||||
}
|
||||
|
||||
static void dis_lsl_imm(dsp_core_t* dsp)
|
||||
{
|
||||
uint32_t D = dsp->disasm_cur_inst & 1;
|
||||
uint32_t ii = (dsp->disasm_cur_inst >> 1) & BITMASK(5);
|
||||
sprintf(dsp->disasm_str_instr, "lsl #$%02x, %s",
|
||||
ii,
|
||||
registers_name[D ? DSP_REG_B : DSP_REG_A]);
|
||||
}
|
||||
|
||||
static void dis_asr_imm(dsp_core_t* dsp)
|
||||
{
|
||||
uint32_t S = (dsp->disasm_cur_inst >> 7) & 1;
|
|
@ -2,6 +2,7 @@
|
|||
* MCPX DSP DMA
|
||||
*
|
||||
* Copyright (c) 2015 espes
|
||||
* Copyright (c) 2020-2021 Matt Borgerson
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -22,7 +23,11 @@
|
|||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include "qemu/compiler.h"
|
||||
#include "dsp_dma.h"
|
||||
#include "dsp_state.h"
|
||||
|
||||
|
||||
#define DMA_CONFIGURATION_AUTOSTART (1 << 0)
|
||||
#define DMA_CONFIGURATION_AUTOREADY (1 << 1)
|
||||
|
@ -84,25 +89,91 @@ const char *format_names[] = {
|
|||
"24 bit lsb", /* 0x6 */
|
||||
"<invalid-0x7>" /* 0x7 */
|
||||
};
|
||||
|
||||
const char *space_names[] = {
|
||||
"x", /* DSP_SPACE_X, 0x0 */
|
||||
"y", /* DSP_SPACE_Y, 0x1 */
|
||||
"p" /* DSP_SPACE_P, 0x2 */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
|
||||
static void scratch_circular_copy(
|
||||
DSPDMAState *s,
|
||||
uint32_t scratch_base,
|
||||
uint32_t *scratch_offset,
|
||||
uint32_t scratch_size,
|
||||
uint32_t transfer_size,
|
||||
uint8_t *scratch_buf,
|
||||
int direction)
|
||||
{
|
||||
if (*scratch_offset >= scratch_size) {
|
||||
// fprintf(stderr, "Initial scratch offset exceeds scratch size! Wrapping\n");
|
||||
*scratch_offset = 0;
|
||||
}
|
||||
|
||||
uint32_t buf_offset = 0;
|
||||
|
||||
while (transfer_size > 0) {
|
||||
size_t bytes_until_wrap = scratch_size - *scratch_offset;
|
||||
size_t chunk_size = MIN(transfer_size, bytes_until_wrap);
|
||||
uint32_t scratch_addr = scratch_base + *scratch_offset;
|
||||
|
||||
// R/W to scratch memory from chunk in buffer
|
||||
s->scratch_rw(s->rw_opaque, &scratch_buf[buf_offset], scratch_addr, chunk_size, direction);
|
||||
|
||||
// Advance scratch pointer, wrap if we've reached the end
|
||||
*scratch_offset += chunk_size;
|
||||
if (*scratch_offset >= scratch_size) {
|
||||
*scratch_offset = 0;
|
||||
}
|
||||
|
||||
transfer_size -= chunk_size;
|
||||
buf_offset += chunk_size;
|
||||
}
|
||||
}
|
||||
|
||||
static void dsp_dma_run(DSPDMAState *s)
|
||||
{
|
||||
if (!(s->control & DMA_CONTROL_RUNNING)
|
||||
|| (s->control & DMA_CONTROL_FROZEN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// DSPState *dsp = container_of(s, DSPState, dma);
|
||||
|
||||
while (!(s->next_block & NODE_POINTER_EOL)) {
|
||||
uint32_t addr = s->next_block & NODE_POINTER_VAL;
|
||||
assert((addr+6) < sizeof(s->core->xram));
|
||||
|
||||
uint32_t next_block = dsp56k_read_memory(s->core, DSP_SPACE_X, addr);
|
||||
uint32_t control = dsp56k_read_memory(s->core, DSP_SPACE_X, addr+1);
|
||||
uint32_t count = dsp56k_read_memory(s->core, DSP_SPACE_X, addr+2);
|
||||
uint32_t dsp_offset = dsp56k_read_memory(s->core, DSP_SPACE_X, addr+3);
|
||||
uint32_t scratch_offset = dsp56k_read_memory(s->core, DSP_SPACE_X, addr+4);
|
||||
uint32_t scratch_base = dsp56k_read_memory(s->core, DSP_SPACE_X, addr+5);
|
||||
uint32_t scratch_size = dsp56k_read_memory(s->core, DSP_SPACE_X, addr+6)+1;
|
||||
// FIXME: Are these block addresses BYTE addresses or WORD addresses?
|
||||
// Need to understand this DMA engine better.
|
||||
uint32_t block_addr;
|
||||
int block_space;
|
||||
if (addr < 0x1800) {
|
||||
assert(addr+6 < 0x1800);
|
||||
block_space = DSP_SPACE_X;
|
||||
block_addr = addr;
|
||||
} else if (addr >= 0x1800 && addr < 0x2000) { //?
|
||||
assert(addr+6 < 0x2000);
|
||||
block_space = DSP_SPACE_Y;
|
||||
block_addr = addr - 0x1800;
|
||||
} else if (addr >= 0x2800 && addr < 0x3800) { //?
|
||||
assert(addr+6 < 0x3800);
|
||||
block_space = DSP_SPACE_P;
|
||||
block_addr = addr - 0x2800;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
uint32_t next_block = dsp56k_read_memory(s->core, block_space, block_addr);
|
||||
uint32_t control = dsp56k_read_memory(s->core, block_space, block_addr+1);
|
||||
uint32_t count = dsp56k_read_memory(s->core, block_space, block_addr+2);
|
||||
uint32_t dsp_offset = dsp56k_read_memory(s->core, block_space, block_addr+3);
|
||||
uint32_t scratch_offset = dsp56k_read_memory(s->core, block_space, block_addr+4);
|
||||
uint32_t scratch_base = dsp56k_read_memory(s->core, block_space, block_addr+5);
|
||||
uint32_t scratch_size = dsp56k_read_memory(s->core, block_space, block_addr+6)+1;
|
||||
|
||||
s->next_block = next_block;
|
||||
if (s->next_block & NODE_POINTER_EOL) {
|
||||
|
@ -110,15 +181,15 @@ static void dsp_dma_run(DSPDMAState *s)
|
|||
}
|
||||
|
||||
/* Decode control word */
|
||||
bool dsp_interleave = (control >> 0) & 1;
|
||||
bool direction = control & NODE_CONTROL_DIRECTION;
|
||||
uint32_t unk2 = (control >> 2) & 0x3;
|
||||
bool buffer_offset_writeback = (control >> 4) & 1;
|
||||
uint32_t buf_id = (control >> 5) & 0xf;
|
||||
bool unk9 = (control >> 9) & 1; /* FIXME: What does this do? */
|
||||
uint32_t format = (control >> 10) & 0x7;
|
||||
bool unk13 = (control >> 13) & 1;
|
||||
uint32_t dsp_step = (control >> 14) & 0x3FF;
|
||||
bool dsp_interleave = (control >> 0) & 1;
|
||||
bool direction = control & NODE_CONTROL_DIRECTION;
|
||||
uint32_t unk2 = (control >> 2) & 0x3;
|
||||
bool buffer_offset_writeback = (control >> 4) & 1;
|
||||
uint32_t buf_id = (control >> 5) & 0xf;
|
||||
// bool unk9 = (control >> 9) & 1; /* FIXME: What does this do? */
|
||||
uint32_t format = (control >> 10) & 0x7;
|
||||
bool unk13 = (control >> 13) & 1;
|
||||
// uint32_t dsp_step = (control >> 14) & 0x3FF; // FIXME
|
||||
|
||||
/* Check for unhandled control settings */
|
||||
assert(unk2 == 0x0);
|
||||
|
@ -130,11 +201,14 @@ static void dsp_dma_run(DSPDMAState *s)
|
|||
|
||||
unsigned int item_size;
|
||||
uint32_t item_mask = 0xffffffff;
|
||||
// bool lsb = (format == 6); // FIXME
|
||||
|
||||
switch(format) {
|
||||
case 1:
|
||||
item_size = 2;
|
||||
item_mask = 0x0000FFFF;
|
||||
break;
|
||||
case 2: //big-endian?
|
||||
case 2:
|
||||
case 6:
|
||||
item_size = 4;
|
||||
item_mask = 0x00FFFFFF;
|
||||
|
@ -145,21 +219,7 @@ static void dsp_dma_run(DSPDMAState *s)
|
|||
break;
|
||||
}
|
||||
|
||||
size_t scratch_addr;
|
||||
if (buf_id == 0xe) { // 'circular'?
|
||||
// assert(scratch_offset == 0);
|
||||
// assert(scratch_offset + count * item_size < scratch_size);
|
||||
if (scratch_offset + count * item_size >= scratch_size) {
|
||||
// This happens during the startup sound effect.
|
||||
// I think it might actually be a bug in the code...
|
||||
DPRINTF("skipping bad dma...\n");
|
||||
continue;
|
||||
}
|
||||
scratch_addr = scratch_base + scratch_offset; //??
|
||||
} else {
|
||||
// assert(buf_id == 0xf) // 'offset'
|
||||
scratch_addr = scratch_offset;
|
||||
}
|
||||
size_t scratch_addr = scratch_base + scratch_offset;
|
||||
|
||||
uint32_t mem_address;
|
||||
int mem_space;
|
||||
|
@ -176,70 +236,61 @@ static void dsp_dma_run(DSPDMAState *s)
|
|||
mem_space = DSP_SPACE_P;
|
||||
mem_address = dsp_offset - 0x2800;
|
||||
} else {
|
||||
fprintf(stderr, "Attempt to access %08x\n", dsp_offset);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
char dsp_space_name = '?';
|
||||
if (mem_space == DSP_SPACE_X) {
|
||||
dsp_space_name = 'x';
|
||||
} else if (mem_space == DSP_SPACE_Y) {
|
||||
dsp_space_name = 'y';
|
||||
} else if (mem_space == DSP_SPACE_P) {
|
||||
dsp_space_name = 'p';
|
||||
}
|
||||
#endif
|
||||
|
||||
DPRINTF("dsp dma block x:$%x (%s)\n"
|
||||
" next-block x:$%x%s\n"
|
||||
" control 0x%06x:\n"
|
||||
" dsp-interleave %d\n"
|
||||
" buffer-offset-writeback %d\n"
|
||||
" buffer 0x%x (%s)\n"
|
||||
" unk9 %d\n"
|
||||
" sample-format 0x%x (%s)\n"
|
||||
" dsp-step 0x%x\n"
|
||||
" sample-count 0x%x\n"
|
||||
" block-count 0x%x channel-count %d\n"
|
||||
" dsp-address 0x%x (%c:$%x)\n"
|
||||
" buffer-offset 0x%x (+ buffer-base 0x%x = 0x%zx)\n"
|
||||
" buffer-size 0x%x\n",
|
||||
addr, direction ? "dsp -> buffer" : "buffer -> dsp",
|
||||
next_block & NODE_POINTER_VAL, s->eol ? " (eol)" : "",
|
||||
control,
|
||||
dsp_interleave,
|
||||
buffer_offset_writeback,
|
||||
buf_id, buffer_names[buf_id],
|
||||
unk9,
|
||||
format, format_names[format],
|
||||
dsp_step,
|
||||
count,
|
||||
block_count,
|
||||
channel_count,
|
||||
dsp_offset, dsp_space_name, mem_address,
|
||||
scratch_offset, scratch_base, scratch_addr,
|
||||
scratch_size);
|
||||
|
||||
|
||||
size_t transfer_size = count * item_size;
|
||||
uint8_t* scratch_buf = calloc(count, item_size);
|
||||
|
||||
// FIXME: Remove this intermediate buffer
|
||||
static uint8_t *scratch_buf = NULL;
|
||||
static ssize_t scratch_buf_size = -1;
|
||||
if (count * item_size > scratch_buf_size) {
|
||||
scratch_buf_size = count * item_size;
|
||||
scratch_buf = malloc(scratch_buf_size);
|
||||
}
|
||||
|
||||
if (direction) {
|
||||
int i;
|
||||
for (i=0; i<count; i++) {
|
||||
uint32_t v = dsp56k_read_memory(s->core,
|
||||
mem_space, mem_address+i);
|
||||
switch(item_size) {
|
||||
case 2:
|
||||
*(uint16_t*)(scratch_buf + i*2) = v;
|
||||
break;
|
||||
case 4:
|
||||
*(uint32_t*)(scratch_buf + i*4) = v;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
if (dsp_interleave) {
|
||||
// FIXME: Above xfer size calculation instead of
|
||||
// overwriting here
|
||||
transfer_size = block_count * item_size * channel_count;
|
||||
|
||||
// Interleave samples
|
||||
for (int i = 0; i < block_count; i++) {
|
||||
for (int ch = 0; ch < channel_count; ch++) {
|
||||
uint32_t v = dsp56k_read_memory(s->core,
|
||||
mem_space, mem_address+ch*block_count+i);
|
||||
switch(item_size) {
|
||||
case 2:
|
||||
*(uint16_t*)(scratch_buf + i*2*channel_count + ch*2) = v >> 8;
|
||||
break;
|
||||
case 4:
|
||||
*(uint32_t*)(scratch_buf + i*4*channel_count + ch*4) = v;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
for (int i = 0; i < count; i++) {
|
||||
uint32_t v = dsp56k_read_memory(s->core, mem_space, mem_address+i);
|
||||
switch(item_size) {
|
||||
case 2:
|
||||
*(uint16_t*)(scratch_buf + i*2) = v >> 8;
|
||||
break;
|
||||
case 4:
|
||||
*(uint32_t*)(scratch_buf + i*4) = v;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* FIXME: Move to function; then reuse for both directions */
|
||||
|
@ -247,16 +298,14 @@ static void dsp_dma_run(DSPDMAState *s)
|
|||
case 0x0:
|
||||
case 0x1:
|
||||
case 0x2:
|
||||
case 0x3: {
|
||||
unsigned int fifo_index = buf_id;
|
||||
s->fifo_rw(s->rw_opaque,
|
||||
scratch_buf, fifo_index, transfer_size, 1);
|
||||
case 0x3:
|
||||
s->fifo_rw(s->rw_opaque, scratch_buf, buf_id, transfer_size, 1);
|
||||
break;
|
||||
}
|
||||
case 0xE:
|
||||
scratch_circular_copy(s, scratch_base, &scratch_offset, scratch_size, transfer_size, scratch_buf, 1);
|
||||
break;
|
||||
case 0xF:
|
||||
s->scratch_rw(s->rw_opaque,
|
||||
scratch_buf, scratch_addr, transfer_size, 1);
|
||||
s->scratch_rw(s->rw_opaque, scratch_buf, scratch_addr, transfer_size, 1);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown DSP DMA buffer: 0x%x\n", buf_id);
|
||||
|
@ -264,20 +313,22 @@ static void dsp_dma_run(DSPDMAState *s)
|
|||
break;
|
||||
}
|
||||
} else {
|
||||
assert(!dsp_interleave);
|
||||
|
||||
/* FIXME: Support FIFOs */
|
||||
assert(buf_id == 0xE || buf_id == 0xF);
|
||||
if (buf_id == 0xe) {
|
||||
scratch_circular_copy(s, scratch_base, &scratch_offset, scratch_size, transfer_size, scratch_buf, 0);
|
||||
} else if (buf_id == 0xf) {
|
||||
s->scratch_rw(s->rw_opaque, scratch_buf, scratch_addr, transfer_size, 0);
|
||||
} else {
|
||||
fprintf(stderr, "Unhandled DSP DMA buffer: 0x%x\n", buf_id);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// read from scratch memory
|
||||
s->scratch_rw(s->rw_opaque,
|
||||
scratch_buf, scratch_addr, transfer_size, 0);
|
||||
|
||||
int i;
|
||||
for (i=0; i<count; i++) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
uint32_t v;
|
||||
switch(item_size) {
|
||||
case 2:
|
||||
v = *(uint16_t*)(scratch_buf + i*2);
|
||||
v = *(uint16_t*)(scratch_buf + i*2) << 8;
|
||||
break;
|
||||
case 4:
|
||||
v = (*(uint32_t*)(scratch_buf + i*4)) & item_mask;
|
||||
|
@ -286,12 +337,14 @@ static void dsp_dma_run(DSPDMAState *s)
|
|||
assert(false);
|
||||
break;
|
||||
}
|
||||
// DPRINTF("... %06x\n", v);
|
||||
|
||||
dsp56k_write_memory(s->core, mem_space, mem_address+i, v);
|
||||
}
|
||||
}
|
||||
|
||||
free(scratch_buf);
|
||||
if (buffer_offset_writeback) {
|
||||
dsp56k_write_memory(s->core, block_space, block_addr+4, scratch_offset);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -340,6 +393,9 @@ void dsp_dma_write(DSPDMAState *s, DSPDMARegister reg, uint32_t v)
|
|||
break;
|
||||
}
|
||||
dsp_dma_run(s);
|
||||
|
||||
// IDK about this, but need to stop somehow?
|
||||
// s->control |= DMA_CONTROL_STOPPED;
|
||||
break;
|
||||
case DMA_START_BLOCK:
|
||||
s->start_block = v;
|
|
@ -26,6 +26,9 @@
|
|||
#include "dsp.h"
|
||||
#include "dsp_cpu.h"
|
||||
|
||||
#define DMA_CONTROL_RUNNING (1 << 4)
|
||||
#define DMA_CONTROL_STOPPED (1 << 5)
|
||||
|
||||
typedef enum DSPDMARegister {
|
||||
DMA_CONFIGURATION,
|
||||
DMA_CONTROL,
|
||||
|
@ -52,4 +55,4 @@ typedef struct DSPDMAState {
|
|||
uint32_t dsp_dma_read(DSPDMAState *s, DSPDMARegister reg);
|
||||
void dsp_dma_write(DSPDMAState *s, DSPDMARegister reg, uint32_t v);
|
||||
|
||||
#endif
|
||||
#endif
|
|
@ -1689,6 +1689,17 @@ static void emu_lsl_b(dsp_core_t* dsp)
|
|||
dsp->registers[DSP_REG_SR] |= (dsp->registers[DSP_REG_B1]==0)<<DSP_SR_Z;
|
||||
}
|
||||
|
||||
static void emu_lsl_imm(dsp_core_t* dsp)
|
||||
{
|
||||
uint32_t D = dsp->cur_inst & 1;
|
||||
uint32_t ii = (dsp->cur_inst >> 1) & BITMASK(5);
|
||||
// FIXME
|
||||
void (*func)(dsp_core_t *dsp) = D ? emu_lsl_b : emu_lsl_a;
|
||||
while (ii--) {
|
||||
func(dsp);
|
||||
}
|
||||
}
|
||||
|
||||
static void emu_lsr_a(dsp_core_t* dsp)
|
||||
{
|
||||
uint32_t newcarry = dsp->registers[DSP_REG_A1] & 1;
|
|
@ -34,6 +34,8 @@ struct DSPState {
|
|||
int save_cycles;
|
||||
|
||||
uint32_t interrupts;
|
||||
|
||||
bool is_gp;
|
||||
};
|
||||
|
||||
#endif /* DSP_STATE_H */
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Helper FP conversions
|
||||
*
|
||||
* Copyright (c) 2020-2021 Matt Borgerson
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FLOATCONV_H
|
||||
#define FLOATCONV_H
|
||||
|
||||
static float uint8_to_float(uint8_t value)
|
||||
{
|
||||
return ((int)value - 0x80) / (1.0 * 0x80);
|
||||
}
|
||||
|
||||
static float int16_to_float(int16_t value)
|
||||
{
|
||||
return value / (1.0 * 0x8000);
|
||||
}
|
||||
|
||||
static float int32_to_float(int32_t value)
|
||||
{
|
||||
return value / (1.0 * 0x80000000);
|
||||
}
|
||||
|
||||
static float int24_to_float(int32_t value)
|
||||
{
|
||||
return int32_to_float((uint32_t)value << 8);
|
||||
}
|
||||
|
||||
static uint32_t float_to_24b(float value)
|
||||
{
|
||||
double scaled_value = value * (8.0 * 0x100000);
|
||||
int int24;
|
||||
if (scaled_value >= (1.0 * 0x7fffff)) {
|
||||
int24 = 0x7fffff;
|
||||
} else if (scaled_value <= (-8.0 * 0x100000)) {
|
||||
int24 = -1 - 0x7fffff;
|
||||
} else {
|
||||
int24 = lrint(scaled_value);
|
||||
}
|
||||
return int24 & 0xffffff;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Adapted from SWH LADSPA Plugins package, modified for xemu
|
||||
*
|
||||
* Source: https://github.com/swh/ladspa/blob/master/svf_1214.xml
|
||||
* Author: Steve Harris, andy@vellocet
|
||||
* License: GPLv2
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SVF_H
|
||||
#define SVF_H
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#define flush_to_zero(x) x
|
||||
|
||||
// Constants to match filter types
|
||||
#define F_LP 1
|
||||
#define F_HP 2
|
||||
#define F_BP 3
|
||||
#define F_BR 4
|
||||
#define F_AP 5
|
||||
|
||||
// Number of filter oversamples
|
||||
#define F_R 1
|
||||
|
||||
/* Structure to hold parameters for SV filter */
|
||||
|
||||
typedef struct {
|
||||
float f; // 2.0*sin(PI*fs/(fc*r));
|
||||
float q; // 2.0*cos(pow(q, 0.1)*PI*0.5);
|
||||
float qnrm; // sqrt(m/2.0f+0.01f);
|
||||
float h; // high pass output
|
||||
float b; // band pass output
|
||||
float l; // low pass output
|
||||
float p; // peaking output (allpass with resonance)
|
||||
float n; // notch output
|
||||
float *op; // pointer to output value
|
||||
} sv_filter;
|
||||
|
||||
/* Store data in SVF struct, takes the sampling frequency, cutoff frequency
|
||||
and Q, and fills in the structure passed */
|
||||
/*
|
||||
static inline void setup_svf(sv_filter *sv, float fs, float fc, float q, int t) {
|
||||
sv->f = 2.0f * sin(M_PI * fc / (float)(fs * F_R));
|
||||
sv->q = 2.0f * cos(pow(q, 0.1f) * M_PI * 0.5f);
|
||||
*/
|
||||
static inline void setup_svf(sv_filter *sv, float fc, float q, int t) {
|
||||
sv->f = fc;
|
||||
sv->q = q;
|
||||
sv->qnrm = sqrt(sv->q/2.0+0.01);
|
||||
switch(t) {
|
||||
case F_LP:
|
||||
sv->op = &(sv->l);
|
||||
break;
|
||||
case F_HP:
|
||||
sv->op = &(sv->h);
|
||||
break;
|
||||
case F_BP:
|
||||
sv->op = &(sv->b);
|
||||
break;
|
||||
case F_BR:
|
||||
sv->op = &(sv->n);
|
||||
break;
|
||||
default:
|
||||
sv->op = &(sv->p);
|
||||
}
|
||||
}
|
||||
|
||||
/* Run one sample through the SV filter. Filter is by andy@vellocet */
|
||||
static inline float run_svf(sv_filter *sv, float in) {
|
||||
float out;
|
||||
int i;
|
||||
in = sv->qnrm * in ;
|
||||
for (i=0; i < F_R; i++) {
|
||||
// very slight waveshape for extra stability
|
||||
sv->b = flush_to_zero(sv->b - sv->b * sv->b * sv->b * 0.001f);
|
||||
// regular state variable code here
|
||||
// the notch and peaking outputs are optional
|
||||
sv->h = flush_to_zero(in - sv->l - sv->q * sv->b);
|
||||
sv->b = sv->b + sv->f * sv->h;
|
||||
sv->l = flush_to_zero(sv->l + sv->f * sv->b);
|
||||
sv->n = sv->l + sv->h;
|
||||
sv->p = sv->l - sv->h;
|
||||
out = *(sv->op);
|
||||
in = out;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif
|
1741
hw/xbox/mcpx_apu.c
1741
hw/xbox/mcpx_apu.c
File diff suppressed because it is too large
Load Diff
|
@ -52,7 +52,7 @@
|
|||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/i2c/smbus_eeprom.h"
|
||||
#include "hw/xbox/nv2a/nv2a.h"
|
||||
#include "hw/xbox/mcpx_apu.h"
|
||||
#include "hw/xbox/mcpx/apu.h"
|
||||
|
||||
#include "hw/xbox/xbox.h"
|
||||
#include "smbus.h"
|
||||
|
|
257
ui/xemu-hud.cc
257
ui/xemu-hud.cc
|
@ -48,6 +48,7 @@ extern "C" {
|
|||
#include "qemu-common.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "hw/xbox/mcpx/apu_debug.h"
|
||||
|
||||
#undef typename
|
||||
#undef atomic_fetch_add
|
||||
|
@ -130,7 +131,7 @@ private:
|
|||
const float fade_in = 0.1;
|
||||
const float fade_out = 0.9;
|
||||
float fade = 0;
|
||||
|
||||
|
||||
if (t < fade_in) {
|
||||
// Linear fade in
|
||||
fade = t/fade_in;
|
||||
|
@ -167,7 +168,7 @@ private:
|
|||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void HelpMarker(const char* desc)
|
||||
|
@ -229,7 +230,7 @@ public:
|
|||
|
||||
const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); // 1 separator, 1 input text
|
||||
ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), false, ImGuiWindowFlags_HorizontalScrollbar); // Leave room for 1 separator + 1 InputText
|
||||
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4,1)); // Tighten spacing
|
||||
ImGui::PushFont(g_fixed_width_font);
|
||||
ImGui::TextUnformatted(xemu_get_monitor_buffer());
|
||||
|
@ -531,6 +532,22 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
static const char *paused_file_open(int flags,
|
||||
const char *filters,
|
||||
const char *default_path,
|
||||
const char *default_name)
|
||||
{
|
||||
bool is_running = runstate_is_running();
|
||||
if (is_running) {
|
||||
vm_stop(RUN_STATE_PAUSED);
|
||||
}
|
||||
const char *r = noc_file_dialog_open(flags, filters, default_path, default_name);
|
||||
if (is_running) {
|
||||
vm_start();
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#define MAX_STRING_LEN 2048 // FIXME: Completely arbitrary and only used here
|
||||
// to give a buffer to ImGui for each field
|
||||
|
@ -557,7 +574,7 @@ public:
|
|||
is_open = false;
|
||||
dirty = false;
|
||||
pending_restart = false;
|
||||
|
||||
|
||||
flash_path[0] = '\0';
|
||||
bootrom_path[0] = '\0';
|
||||
hdd_path[0] = '\0';
|
||||
|
@ -565,7 +582,7 @@ public:
|
|||
memory_idx = 0;
|
||||
short_animation = false;
|
||||
}
|
||||
|
||||
|
||||
~SettingsWindow()
|
||||
{
|
||||
}
|
||||
|
@ -595,7 +612,7 @@ public:
|
|||
len = strlen(tmp);
|
||||
assert(len < MAX_STRING_LEN);
|
||||
strncpy(eeprom_path, tmp, sizeof(eeprom_path));
|
||||
|
||||
|
||||
xemu_settings_get_int(XEMU_SETTINGS_SYSTEM_MEMORY, &tmp_int);
|
||||
memory_idx = (tmp_int-64)/64;
|
||||
|
||||
|
@ -626,7 +643,7 @@ public:
|
|||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Browse...", ImVec2(100*g_ui_scale, 0))) {
|
||||
const char *selected = noc_file_dialog_open(NOC_FILE_DIALOG_OPEN, filters, buf, NULL);
|
||||
const char *selected = paused_file_open(NOC_FILE_DIALOG_OPEN, filters, buf, NULL);
|
||||
if ((selected != NULL) && (strcmp(buf, selected) != 0)) {
|
||||
strncpy(buf, selected, len-1);
|
||||
dirty = true;
|
||||
|
@ -740,7 +757,7 @@ public:
|
|||
// FIXME: Show driver
|
||||
// FIXME: Show BIOS/BootROM hash
|
||||
}
|
||||
|
||||
|
||||
~AboutWindow()
|
||||
{
|
||||
}
|
||||
|
@ -786,7 +803,7 @@ public:
|
|||
|
||||
ImGui::SetCursorPosX(10*g_ui_scale);
|
||||
ImGui::Dummy(ImVec2(0,20*g_ui_scale));
|
||||
|
||||
|
||||
const char *msg = "Visit https://xemu.app for more information";
|
||||
ImGui::SetCursorPosX((ImGui::GetWindowWidth()-ImGui::CalcTextSize(msg).x)/2);
|
||||
ImGui::Text("%s", msg);
|
||||
|
@ -910,7 +927,7 @@ public:
|
|||
xemu_settings_set_bool(XEMU_SETTINGS_NETWORK_ENABLED, xemu_net_is_enabled());
|
||||
xemu_settings_save();
|
||||
}
|
||||
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
};
|
||||
|
@ -1052,7 +1069,7 @@ public:
|
|||
|
||||
ImGui::Columns(2, "", false);
|
||||
ImGui::SetColumnWidth(0, ImGui::GetWindowWidth()*0.25);
|
||||
|
||||
|
||||
ImGui::Text("User Token");
|
||||
ImGui::SameLine();
|
||||
HelpMarker("This is a unique access token used to authorize submission of the report. To request a token, click 'Get Token'.");
|
||||
|
@ -1082,9 +1099,9 @@ public:
|
|||
ImGui::SameLine();
|
||||
HelpMarker(playability_descriptions[playability]);
|
||||
ImGui::NextColumn();
|
||||
|
||||
|
||||
ImGui::Columns(1);
|
||||
|
||||
|
||||
ImGui::Text("Description");
|
||||
if (ImGui::InputTextMultiline("###desc", description, sizeof(description), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 6), 0)) {
|
||||
report.compat_comments = description;
|
||||
|
@ -1117,10 +1134,10 @@ public:
|
|||
"This information will be archived and used to analyze, resolve problems with, "
|
||||
"and improve the application. This information may be made publicly visible, "
|
||||
"for example: to anyone who wishes to see the playability status of a title, as "
|
||||
"indicated by your report.");
|
||||
"indicated by your report.");
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
|
||||
ImGui::Dummy(ImVec2(0.0f, ImGui::GetStyle().WindowPadding.y));
|
||||
ImGui::Separator();
|
||||
ImGui::Dummy(ImVec2(0.0f, ImGui::GetStyle().WindowPadding.y));
|
||||
|
@ -1149,12 +1166,210 @@ public:
|
|||
xemu_settings_save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
};
|
||||
|
||||
#include <math.h>
|
||||
|
||||
float mix(float a, float b, float t)
|
||||
{
|
||||
return a*(1.0-t) + (b-a)*t;
|
||||
}
|
||||
|
||||
class DebugApuWindow
|
||||
{
|
||||
public:
|
||||
bool is_open;
|
||||
|
||||
DebugApuWindow()
|
||||
{
|
||||
is_open = false;
|
||||
}
|
||||
|
||||
~DebugApuWindow()
|
||||
{
|
||||
}
|
||||
|
||||
void Draw()
|
||||
{
|
||||
if (!is_open) return;
|
||||
|
||||
ImGui::SetNextWindowContentSize(ImVec2(600.0f*g_ui_scale, 0.0f));
|
||||
if (!ImGui::Begin("Audio Debug", &is_open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
const struct McpxApuDebug *dbg = mcpx_apu_get_debug_info();
|
||||
|
||||
|
||||
ImGui::Columns(2, "", false);
|
||||
int now = SDL_GetTicks() % 1000;
|
||||
float t = now/1000.0f;
|
||||
float freq = 1;
|
||||
float v = fabs(sin(M_PI*t*freq));
|
||||
float c_active = mix(0.4, 0.97, v);
|
||||
float c_inactive = 0.2f;
|
||||
|
||||
int voice_monitor = -1;
|
||||
int voice_info = -1;
|
||||
int voice_mute = -1;
|
||||
|
||||
// Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style.
|
||||
ImGui::PushFont(g_fixed_width_font);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 4));
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
if (i % 16) {
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
float c, s, h;
|
||||
h = 0.6;
|
||||
if (dbg->vp.v[i].active) {
|
||||
if (dbg->vp.v[i].paused) {
|
||||
c = c_inactive;
|
||||
s = 0.4;
|
||||
} else {
|
||||
c = c_active;
|
||||
s = 0.7;
|
||||
}
|
||||
if (mcpx_apu_debug_is_muted(i)) {
|
||||
h = 1.0;
|
||||
}
|
||||
} else {
|
||||
c = c_inactive;
|
||||
s = 0;
|
||||
}
|
||||
|
||||
ImGui::PushID(i);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(h, s, c));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(h, s, 0.8));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(h, 0.8f, 1.0));
|
||||
char buf[12];
|
||||
snprintf(buf, sizeof(buf), "%02x", i);
|
||||
ImGui::Button(buf);
|
||||
if (/*dbg->vp.v[i].active &&*/ ImGui::IsItemHovered()) {
|
||||
voice_monitor = i;
|
||||
voice_info = i;
|
||||
}
|
||||
if (ImGui::IsItemClicked(1)) {
|
||||
voice_mute = i;
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopFont();
|
||||
|
||||
if (voice_info >= 0) {
|
||||
const struct McpxApuDebugVoice *voice = &dbg->vp.v[voice_info];
|
||||
ImGui::BeginTooltip();
|
||||
bool is_paused = voice->paused;
|
||||
ImGui::Text("Voice 0x%x/%d %s", voice_info, voice_info, is_paused ? "(Paused)" : "");
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(voice->stereo ? "Stereo" : "Mono");
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::PushFont(g_fixed_width_font);
|
||||
|
||||
const char *noyes[2] = { "NO", "YES" };
|
||||
ImGui::Text("Stream: %-3s Loop: %-3s Persist: %-3s Multipass: %-3s "
|
||||
"Linked: %-3s",
|
||||
noyes[voice->stream], noyes[voice->loop],
|
||||
noyes[voice->persist], noyes[voice->multipass],
|
||||
noyes[voice->linked]);
|
||||
|
||||
const char *cs[4] = { "1 byte", "2 bytes", "ADPCM", "4 bytes" };
|
||||
const char *ss[4] = {
|
||||
"Unsigned 8b PCM",
|
||||
"Signed 16b PCM",
|
||||
"Signed 24b PCM",
|
||||
"Signed 32b PCM"
|
||||
};
|
||||
|
||||
assert(voice->container_size < 4);
|
||||
assert(voice->sample_size < 4);
|
||||
ImGui::Text("Container Size: %s, Sample Size: %s, Samples per Block: %d",
|
||||
cs[voice->container_size], ss[voice->sample_size], voice->samples_per_block);
|
||||
ImGui::Text("Rate: %f (%d Hz)", voice->rate, (int)(48000.0/voice->rate));
|
||||
ImGui::Text("EBO=%d CBO=%d LBO=%d BA=%x",
|
||||
voice->ebo, voice->cbo, voice->lbo, voice->ba);
|
||||
ImGui::Text("Mix: ");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (i == 4) ImGui::Text(" ");
|
||||
ImGui::SameLine();
|
||||
char buf[64];
|
||||
if (voice->vol[i] == 0xFFF) {
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Bin %2d (MUTE) ", voice->bin[i]);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Bin %2d (-%.3f) ", voice->bin[i],
|
||||
(float)((voice->vol[i] >> 6) & 0x3f) +
|
||||
(float)((voice->vol[i] >> 0) & 0x3f) / 64.0);
|
||||
}
|
||||
ImGui::Text("%-17s", buf);
|
||||
}
|
||||
ImGui::PopFont();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
if (voice_monitor >= 0) {
|
||||
mcpx_apu_debug_isolate_voice(voice_monitor);
|
||||
} else {
|
||||
mcpx_apu_debug_clear_isolations();
|
||||
}
|
||||
if (voice_mute >= 0) {
|
||||
mcpx_apu_debug_toggle_mute(voice_mute);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SetColumnWidth(0, ImGui::GetCursorPosX());
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::PushFont(g_fixed_width_font);
|
||||
ImGui::Text("Frames: %04d", dbg->frames_processed);
|
||||
ImGui::Text("GP Cycles: %04d", dbg->gp.cycles);
|
||||
ImGui::Text("EP Cycles: %04d", dbg->ep.cycles);
|
||||
bool color = (dbg->utilization > 0.9);
|
||||
if (color) ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,0,0,1));
|
||||
ImGui::Text("Utilization: %.2f%%", (dbg->utilization*100));
|
||||
if (color) ImGui::PopStyleColor();
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
static int mon = 0;
|
||||
mon = mcpx_apu_debug_get_monitor();
|
||||
if (ImGui::Combo("Monitor", &mon, "AC97\0VP Only\0GP Only\0EP Only\0GP/EP if enabled\0")) {
|
||||
mcpx_apu_debug_set_monitor(mon);
|
||||
}
|
||||
|
||||
static bool gp_realtime;
|
||||
gp_realtime = dbg->gp_realtime;
|
||||
if (ImGui::Checkbox("GP Realtime\n", &gp_realtime)) {
|
||||
mcpx_apu_debug_set_gp_realtime_enabled(gp_realtime);
|
||||
}
|
||||
|
||||
static bool ep_realtime;
|
||||
ep_realtime = dbg->ep_realtime;
|
||||
if (ImGui::Checkbox("EP Realtime\n", &ep_realtime)) {
|
||||
mcpx_apu_debug_set_ep_realtime_enabled(ep_realtime);
|
||||
}
|
||||
|
||||
ImGui::Columns(1);
|
||||
ImGui::End();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static MonitorWindow monitor_window;
|
||||
static DebugApuWindow apu_window;
|
||||
static InputWindow input_window;
|
||||
static NetworkWindow network_window;
|
||||
static AboutWindow about_window;
|
||||
|
@ -1172,7 +1387,7 @@ public:
|
|||
{
|
||||
is_open = false;
|
||||
}
|
||||
|
||||
|
||||
~FirstBootWindow()
|
||||
{
|
||||
}
|
||||
|
@ -1240,7 +1455,7 @@ public:
|
|||
if (ImGui::IsItemClicked()) {
|
||||
xemu_open_web_browser("https://xemu.app");
|
||||
}
|
||||
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
};
|
||||
|
@ -1265,7 +1480,7 @@ static void action_load_disc(void)
|
|||
const char *iso_file_filters = ".iso Files\0*.iso\0All Files\0*.*\0";
|
||||
const char *current_disc_path;
|
||||
xemu_settings_get_string(XEMU_SETTINGS_SYSTEM_DVD_PATH, ¤t_disc_path);
|
||||
const char *new_disc_path = noc_file_dialog_open(NOC_FILE_DIALOG_OPEN, iso_file_filters, current_disc_path, NULL);
|
||||
const char *new_disc_path = paused_file_open(NOC_FILE_DIALOG_OPEN, iso_file_filters, current_disc_path, NULL);
|
||||
if (new_disc_path == NULL) {
|
||||
/* Cancelled */
|
||||
return;
|
||||
|
@ -1385,6 +1600,7 @@ static void ShowMainMenu()
|
|||
if (ImGui::BeginMenu("Debug"))
|
||||
{
|
||||
ImGui::MenuItem("Monitor", NULL, &monitor_window.is_open);
|
||||
ImGui::MenuItem("Audio", NULL, &apu_window.is_open);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
|
@ -1530,7 +1746,7 @@ void xemu_hud_should_capture_kbd_mouse(int *kbd, int *mouse)
|
|||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (kbd) *kbd = io.WantCaptureKeyboard;
|
||||
if (mouse) *mouse = io.WantCaptureMouse;
|
||||
if (mouse) *mouse = io.WantCaptureMouse;
|
||||
}
|
||||
|
||||
void xemu_hud_render(void)
|
||||
|
@ -1670,6 +1886,7 @@ void xemu_hud_render(void)
|
|||
input_window.Draw();
|
||||
settings_window.Draw();
|
||||
monitor_window.Draw();
|
||||
apu_window.Draw();
|
||||
about_window.Draw();
|
||||
network_window.Draw();
|
||||
compatibility_reporter_window.Draw();
|
||||
|
|
|
@ -44,6 +44,9 @@ struct xemu_settings {
|
|||
int memory;
|
||||
int short_animation; // Boolean
|
||||
|
||||
// [audio]
|
||||
int use_dsp; // Boolean
|
||||
|
||||
// [display]
|
||||
int scale;
|
||||
int ui_scale;
|
||||
|
@ -102,6 +105,8 @@ struct config_offset_table {
|
|||
[XEMU_SETTINGS_SYSTEM_EEPROM_PATH] = { CONFIG_TYPE_STRING, "system", "eeprom_path", offsetof(struct xemu_settings, eeprom_path), { .default_str = "" } },
|
||||
[XEMU_SETTINGS_SYSTEM_MEMORY] = { CONFIG_TYPE_INT, "system", "memory", offsetof(struct xemu_settings, memory), { .default_int = 64 } },
|
||||
[XEMU_SETTINGS_SYSTEM_SHORTANIM] = { CONFIG_TYPE_BOOL, "system", "shortanim", offsetof(struct xemu_settings, short_animation), { .default_bool = 0 } },
|
||||
|
||||
[XEMU_SETTINGS_AUDIO_USE_DSP] = { CONFIG_TYPE_BOOL, "audio", "use_dsp", offsetof(struct xemu_settings, use_dsp), { .default_bool = 0 } },
|
||||
|
||||
[XEMU_SETTINGS_DISPLAY_SCALE] = { CONFIG_TYPE_ENUM, "display", "scale", offsetof(struct xemu_settings, scale), { .default_int = DISPLAY_SCALE_SCALE }, display_scale_map },
|
||||
[XEMU_SETTINGS_DISPLAY_UI_SCALE] = { CONFIG_TYPE_INT, "display", "ui_scale", offsetof(struct xemu_settings, ui_scale), { .default_int = 1 } },
|
||||
|
|
|
@ -36,6 +36,7 @@ enum xemu_settings_keys {
|
|||
XEMU_SETTINGS_SYSTEM_DVD_PATH,
|
||||
XEMU_SETTINGS_SYSTEM_MEMORY,
|
||||
XEMU_SETTINGS_SYSTEM_SHORTANIM,
|
||||
XEMU_SETTINGS_AUDIO_USE_DSP,
|
||||
XEMU_SETTINGS_DISPLAY_SCALE,
|
||||
XEMU_SETTINGS_DISPLAY_UI_SCALE,
|
||||
XEMU_SETTINGS_INPUT_CONTROLLER_1_GUID,
|
||||
|
|
Loading…
Reference in New Issue