mcpx: Enable APU pipeline

This commit is contained in:
Matt Borgerson 2020-12-18 01:25:28 -07:00 committed by mborgerson
parent 9004009bb3
commit 416e12c3b3
30 changed files with 3988 additions and 2112 deletions

View File

@ -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

9
configure vendored
View File

@ -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

View File

@ -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;
}
}

View File

@ -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/

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -0,0 +1,4 @@
obj-y += apu.o aci.o
apu.o-cflags := $(SDL_CFLAGS)
obj-y += dsp/

View File

@ -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

143
hw/xbox/mcpx/adpcm.h Normal file
View File

@ -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

2638
hw/xbox/mcpx/apu.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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

90
hw/xbox/mcpx/apu_debug.h Normal file
View File

@ -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

346
hw/xbox/mcpx/apu_regs.h Normal file
View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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];
/**********************************

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -34,6 +34,8 @@ struct DSPState {
int save_cycles;
uint32_t interrupts;
bool is_gp;
};
#endif /* DSP_STATE_H */

58
hw/xbox/mcpx/fpconv.h Normal file
View File

@ -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

91
hw/xbox/mcpx/svf.h Normal file
View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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"

View File

@ -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, &current_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();

View File

@ -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 } },

View File

@ -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,