Merge branch 'master' into ohci_fix

This commit is contained in:
Fred Hallock 2025-04-03 19:47:06 -04:00 committed by GitHub
commit b71d663102
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
44 changed files with 842 additions and 647 deletions

8
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "ci"

View File

@ -21,10 +21,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Clone tree
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Extract image metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
labels: |
@ -35,16 +35,16 @@ jobs:
type=ref,event=branch
type=sha
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v5
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v5
with:
context: ubuntu-win64-cross
push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}

View File

@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Clone tree
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Install dependencies
@ -50,7 +50,7 @@ jobs:
./scripts/archive-source.sh src.tar
gzip -1 src.tar
- name: Upload source package artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: src.tar.gz
path: src.tar.gz
@ -79,18 +79,18 @@ jobs:
artifact_name: xemu-win-aarch64-release
arch: aarch64
env:
DOCKER_IMAGE_NAME: ghcr.io/xemu-project/xemu-win64-toolchain:sha-3bdb9e7
DOCKER_IMAGE_NAME: ghcr.io/xemu-project/xemu-win64-toolchain:sha-0d06ce8
steps:
- name: Download source package
uses: actions/download-artifact@v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
with:
name: src.tar.gz
- name: Extract source package
run: tar xf src.tar.gz
- name: Initialize compiler cache
id: cache
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
with:
path: /tmp/xemu-ccache
key: cache-wincross-${{ runner.os }}-${{ matrix.arch }}-${{ matrix.configuration }}-${{ github.sha }}
@ -111,7 +111,7 @@ jobs:
$DOCKER_IMAGE_NAME \
bash -c "ccache -z; ./build.sh -p win64-cross ${{ matrix.build_param }} && ccache -s"
- name: Upload build artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ matrix.artifact_name }}
path: dist
@ -140,7 +140,7 @@ jobs:
arch: aarch64
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
with:
name: ${{ matrix.artifact_name }}
path: ${{ matrix.artifact_name }}
@ -157,7 +157,7 @@ jobs:
7z a -tzip ../dist/${{ matrix.artifact_name }}.zip * "-xr!*.pdb"
7z a -tzip ../dist/${{ matrix.artifact_name }}-pdb.zip "-ir!*.pdb"
- name: Upload build artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ matrix.artifact_name }}-pdb
path: dist
@ -196,13 +196,13 @@ jobs:
steps:
- name: Initialize compiler cache
id: cache
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
with:
path: /tmp/xemu-ccache
key: cache-${{ runner.os }}-${{ matrix.arch }}-${{ matrix.configuration }}-${{ github.sha }}
restore-keys: cache-${{ runner.os }}-${{ matrix.arch }}-${{ matrix.configuration }}-
- name: Download source package
uses: actions/download-artifact@v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
with:
name: src.tar.gz
- name: Extract source package
@ -245,6 +245,11 @@ jobs:
echo -e "\n\nCompiler Cache Stats:"
ccache -s
- name: Test
run: |
pushd src/build
./pyvenv/bin/meson test --suite xbox
popd
- name: Generate AppImage
run: |
wget --no-verbose https://github.com/linuxdeploy/linuxdeploy/releases/latest/download/linuxdeploy-${{ matrix.arch }}.AppImage
@ -266,7 +271,7 @@ jobs:
run: |
tar -czvf ${{ matrix.artifact_filename }} --transform "s#^dist#xemu#" dist
- name: Upload build artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ matrix.artifact_name }}
path: ${{ matrix.artifact_filename }}
@ -300,12 +305,12 @@ jobs:
artifact_filename: xemu-macos-arm64-release.zip
steps:
- name: Download source package
uses: actions/download-artifact@v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
with:
name: src.tar.gz
- name: Extract source package
run: tar xf src.tar.gz
- uses: actions/setup-python@v5.1.0
- uses: actions/setup-python@v5.5.0
with:
python-version: '3.12'
- name: Install dependencies
@ -319,7 +324,7 @@ jobs:
pip install pyyaml requests
- name: Initialize compiler, library cache
id: cache
uses: actions/cache@v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
with:
path: |
xemu-ccache
@ -339,7 +344,7 @@ jobs:
zip -r ../${{ matrix.artifact_filename }} *
popd
- name: Upload build artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ matrix.artifact_name }}
path: ${{ matrix.artifact_filename }}
@ -353,12 +358,12 @@ jobs:
configuration: ["debug", "release"]
steps:
- name: Download x86_64 build
uses: actions/download-artifact@v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
with:
name: xemu-macos-x86_64-${{ matrix.configuration }}
path: xemu-macos-x86_64-${{ matrix.configuration }}
- name: Download arm64 build
uses: actions/download-artifact@v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
with:
name: xemu-macos-arm64-${{ matrix.configuration }}
path: xemu-macos-arm64-${{ matrix.configuration }}
@ -382,7 +387,7 @@ jobs:
zip -r ../xemu-macos-universal-${{ matrix.configuration }}.zip *
popd
- name: Upload build artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: xemu-macos-universal-${{ matrix.configuration }}
path: xemu-macos-universal-${{ matrix.configuration }}.zip
@ -393,7 +398,7 @@ jobs:
needs: [Ubuntu, macOSUniversal, Windows, WindowsPdb]
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
with:
path: dist
- name: Extract source package
@ -415,7 +420,7 @@ jobs:
run: |
cp dist/xemu-win-x86_64-release-pdb/xemu-win-x86_64-release.zip dist/xemu-win-x86_64-release-pdb/xemu-win-release.zip
- name: Publish release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1
with:
tag_name: v${{ env.XEMU_VERSION }}
name: v${{ env.XEMU_VERSION }}
@ -439,7 +444,7 @@ jobs:
dist/xemu-ubuntu-aarch64-debug/xemu/xemu-v${{ env.XEMU_VERSION }}-dbg-aarch64.AppImage
dist/xemu-ubuntu-aarch64-release/xemu/xemu-v${{ env.XEMU_VERSION }}-aarch64.AppImage
- name: Trigger website update
uses: benc-uk/workflow-dispatch@v1.2.2
uses: benc-uk/workflow-dispatch@e2e5e9a103e331dad343f381a29e654aea3cf8fc # v1.2.4
with:
workflow: build.yml
repo: xemu-project/xemu-website
@ -457,7 +462,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download source package
uses: actions/download-artifact@v4
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
with:
name: src.tar.gz
- name: Extract source package
@ -476,7 +481,7 @@ jobs:
-- Matt Borgerson <contact@mborgerson.com> $(date -R)" > debian/changelog
popd
- name: Deploy source archive to branch
uses: peaceiris/actions-gh-pages@v3
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./src

1
.gitignore vendored
View File

@ -6,7 +6,6 @@
.sdk
.stgit-*
.git-submodule-status
.clang-format
.gdb_history
cscope.*
tags

2
debian/control vendored
View File

@ -6,6 +6,8 @@ Build-Depends: debhelper (>= 11),
cmake,
git,
python3:any,
python3-pip,
python3-tomli,
python3-yaml,
python3-venv,
ninja-build,

View File

@ -154,6 +154,14 @@ typedef struct MCPXAPUState {
int16_t apu_fifo_output[256][2]; // 1 EP frame (0x400 bytes), 8 buffered
} MCPXAPUState;
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
};
static MCPXAPUState *g_state; // Used via debug handlers
static struct McpxApuDebug g_dbg, g_dbg_cache;
static int g_dbg_voice_monitor = -1;
@ -2571,7 +2579,7 @@ const VMStateDescription vmstate_vp_dsp_core_state = {
VMSTATE_UINT32(num_inst, dsp_core_t),
VMSTATE_UINT32(cur_inst_len, dsp_core_t),
VMSTATE_UINT32(cur_inst, dsp_core_t),
VMSTATE_BOOL(executing_for_disasm, dsp_core_t),
VMSTATE_UNUSED(1),
VMSTATE_UINT32(disasm_memory_ptr, dsp_core_t),
VMSTATE_BOOL(exception_debugging, dsp_core_t),
VMSTATE_UINT32(disasm_prev_inst_pc, dsp_core_t),

View File

@ -22,6 +22,8 @@
#ifndef MCPX_APU_REGS_H
#define MCPX_APU_REGS_H
// clang-format off
#define NV_PAPU_ISTS 0x00001000
# define NV_PAPU_ISTS_GINTSTS (1 << 0)
# define NV_PAPU_ISTS_FETINTSTS (1 << 4)
@ -122,15 +124,6 @@
#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
@ -343,4 +336,6 @@ enum MCPX_HW_NOTIFIER {
#define NV1BA0_NOTIFICATION_STATUS_DONE_SUCCESS 0x01
#define NV1BA0_NOTIFICATION_STATUS_IN_PROGRESS 0x80
// clang-format on
#endif

299
hw/xbox/mcpx/dsp/debug.c Normal file
View File

@ -0,0 +1,299 @@
/*
* MCPX DSP emulator
*
* Copyright (c) 2015 espes
* Copyright (c) 2020-2025 Matt Borgerson
*
* Adapted from Hatari DSP M56001 emulation
* (C) 2001-2008 ARAnyM developer team
* Adaption to Hatari (C) 2008 by Thomas Huth
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "qemu/osdep.h"
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include "dsp_cpu.h"
#include "dsp_dma.h"
#include "dsp_state.h"
#include "dsp.h"
#include "debug.h"
#if DEBUG_DSP
#define BITMASK(x) ((1<<(x))-1)
/**
* Output memory values between given addresses in given DSP address space.
* Return next DSP address value.
*/
uint32_t dsp_disasm_memory(DSPState* dsp, uint32_t dsp_memdump_addr, uint32_t dsp_memdump_upper, char space)
{
uint32_t mem, value;
for (mem = dsp_memdump_addr; mem <= dsp_memdump_upper; mem++) {
value = dsp_read_memory(dsp, space, mem);
printf("%04x %06x\n", mem, value);
}
return dsp_memdump_upper+1;
}
/**
* Show information on DSP core state which isn't
* shown by any of the other commands (dd, dm, dr).
*/
void dsp_info(DSPState* dsp)
{
int i, j;
const char *stackname[] = { "SSH", "SSL" };
printf("DSP core information:\n");
for (i = 0; i < ARRAY_SIZE(stackname); i++) {
printf("- %s stack:", stackname[i]);
for (j = 0; j < ARRAY_SIZE(dsp->core.stack[0]); j++) {
printf(" %04x", dsp->core.stack[i][j]);
}
printf("\n");
}
printf("- Interrupt IPL:");
for (i = 0; i < ARRAY_SIZE(dsp->core.interrupt_ipl); i++) {
printf(" %04x", dsp->core.interrupt_ipl[i]);
}
printf("\n");
printf("- Pending ints: ");
for (i = 0; i < ARRAY_SIZE(dsp->core.interrupt_is_pending); i++) {
printf(" %04hx", dsp->core.interrupt_is_pending[i]);
}
printf("\n");
}
/**
* Show DSP register contents
*/
void dsp_print_registers(DSPState* dsp)
{
uint32_t i;
printf("A: A2: %02x A1: %06x A0: %06x\n",
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",
i, dsp->core.registers[DSP_REG_R0+i],
i, dsp->core.registers[DSP_REG_N0+i],
i, dsp->core.registers[DSP_REG_M0+i]);
}
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",
dsp->core.registers[DSP_REG_SP], dsp->core.registers[DSP_REG_SSH], dsp->core.registers[DSP_REG_SSL]);
}
/**
* Get given DSP register address and required bit mask.
* Works for A0-2, B0-2, LA, LC, M0-7, N0-7, R0-7, X0-1, Y0-1, PC, SR, SP,
* OMR, SSH & SSL registers, but note that the SP, SSH & SSL registers
* need special handling (in DSP*SetRegister()) when they are set.
* Return the register width in bits or zero for an error.
*/
int dsp_get_register_address(DSPState* dsp, const char *regname, uint32_t **addr, uint32_t *mask)
{
#define MAX_REGNAME_LEN 4
typedef struct {
const char name[MAX_REGNAME_LEN];
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[] = {
/* 56-bit A register */
{ "A0", &dsp->core.registers[DSP_REG_A0], 32, BITMASK(24) },
{ "A1", &dsp->core.registers[DSP_REG_A1], 32, BITMASK(24) },
{ "A2", &dsp->core.registers[DSP_REG_A2], 32, BITMASK(8) },
/* 56-bit B register */
{ "B0", &dsp->core.registers[DSP_REG_B0], 32, BITMASK(24) },
{ "B1", &dsp->core.registers[DSP_REG_B1], 32, BITMASK(24) },
{ "B2", &dsp->core.registers[DSP_REG_B2], 32, BITMASK(8) },
/* 16-bit LA & LC registers */
{ "LA", &dsp->core.registers[DSP_REG_LA], 32, BITMASK(16) },
{ "LC", &dsp->core.registers[DSP_REG_LC], 32, BITMASK(16) },
/* 16-bit M registers */
{ "M0", &dsp->core.registers[DSP_REG_M0], 32, BITMASK(16) },
{ "M1", &dsp->core.registers[DSP_REG_M1], 32, BITMASK(16) },
{ "M2", &dsp->core.registers[DSP_REG_M2], 32, BITMASK(16) },
{ "M3", &dsp->core.registers[DSP_REG_M3], 32, BITMASK(16) },
{ "M4", &dsp->core.registers[DSP_REG_M4], 32, BITMASK(16) },
{ "M5", &dsp->core.registers[DSP_REG_M5], 32, BITMASK(16) },
{ "M6", &dsp->core.registers[DSP_REG_M6], 32, BITMASK(16) },
{ "M7", &dsp->core.registers[DSP_REG_M7], 32, BITMASK(16) },
/* 16-bit N registers */
{ "N0", &dsp->core.registers[DSP_REG_N0], 32, BITMASK(16) },
{ "N1", &dsp->core.registers[DSP_REG_N1], 32, BITMASK(16) },
{ "N2", &dsp->core.registers[DSP_REG_N2], 32, BITMASK(16) },
{ "N3", &dsp->core.registers[DSP_REG_N3], 32, BITMASK(16) },
{ "N4", &dsp->core.registers[DSP_REG_N4], 32, BITMASK(16) },
{ "N5", &dsp->core.registers[DSP_REG_N5], 32, BITMASK(16) },
{ "N6", &dsp->core.registers[DSP_REG_N6], 32, BITMASK(16) },
{ "N7", &dsp->core.registers[DSP_REG_N7], 32, BITMASK(16) },
{ "OMR", &dsp->core.registers[DSP_REG_OMR], 32, 0x5f },
/* 16-bit program counter */
{ "PC", (uint32_t*)(&dsp->core.pc), 24, BITMASK(24) },
/* 16-bit DSP R (address) registers */
{ "R0", &dsp->core.registers[DSP_REG_R0], 32, BITMASK(16) },
{ "R1", &dsp->core.registers[DSP_REG_R1], 32, BITMASK(16) },
{ "R2", &dsp->core.registers[DSP_REG_R2], 32, BITMASK(16) },
{ "R3", &dsp->core.registers[DSP_REG_R3], 32, BITMASK(16) },
{ "R4", &dsp->core.registers[DSP_REG_R4], 32, BITMASK(16) },
{ "R5", &dsp->core.registers[DSP_REG_R5], 32, BITMASK(16) },
{ "R6", &dsp->core.registers[DSP_REG_R6], 32, BITMASK(16) },
{ "R7", &dsp->core.registers[DSP_REG_R7], 32, BITMASK(16) },
{ "SSH", &dsp->core.registers[DSP_REG_SSH], 32, BITMASK(16) },
{ "SSL", &dsp->core.registers[DSP_REG_SSL], 32, BITMASK(16) },
{ "SP", &dsp->core.registers[DSP_REG_SP], 32, BITMASK(6) },
/* 16-bit status register */
{ "SR", &dsp->core.registers[DSP_REG_SR], 32, 0xefff },
/* 48-bit X register */
{ "X0", &dsp->core.registers[DSP_REG_X0], 32, BITMASK(24) },
{ "X1", &dsp->core.registers[DSP_REG_X1], 32, BITMASK(24) },
/* 48-bit Y register */
{ "Y0", &dsp->core.registers[DSP_REG_Y0], 32, BITMASK(24) },
{ "Y1", &dsp->core.registers[DSP_REG_Y1], 32, BITMASK(24) }
};
/* left, right, middle, direction */
int l, r, m, dir = 0;
unsigned int i, len;
char reg[MAX_REGNAME_LEN];
for (i = 0; i < sizeof(reg) && regname[i]; i++) {
reg[i] = toupper(regname[i]);
}
if (i < 2 || regname[i]) {
/* too short or longer than any of the names */
return 0;
}
len = i;
/* bisect */
l = 0;
r = ARRAY_SIZE(registers) - 1;
do {
m = (l+r) >> 1;
for (i = 0; i < len; i++) {
dir = (int)reg[i] - registers[m].name[i];
if (dir) {
break;
}
}
if (dir == 0) {
*addr = registers[m].addr;
*mask = registers[m].mask;
return registers[m].bits;
}
if (dir < 0) {
r = m-1;
} else {
l = m+1;
}
} while (l <= r);
#undef MAX_REGNAME_LEN
return 0;
}
/**
* Set given DSP register value, return false if unknown register given
*/
bool dsp_disasm_set_register(DSPState* dsp, const char *arg, uint32_t value)
{
uint32_t *addr, mask, sp_value;
int bits;
/* first check registers needing special handling... */
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);
dsp->core.registers[DSP_REG_SSH] = dsp->core.stack[0][value];
dsp->core.registers[DSP_REG_SSL] = dsp->core.stack[1][value];
return true;
}
if (arg[1]=='S' || arg[1]=='s') {
sp_value = dsp->core.registers[DSP_REG_SP] & BITMASK(4);
if (arg[2]=='H' || arg[2]=='h') {
if (sp_value == 0) {
dsp->core.registers[DSP_REG_SSH] = 0;
dsp->core.stack[0][sp_value] = 0;
} else {
dsp->core.registers[DSP_REG_SSH] = value & BITMASK(16);
dsp->core.stack[0][sp_value] = value & BITMASK(16);
}
return true;
}
if (arg[2]=='L' || arg[2]=='l') {
if (sp_value == 0) {
dsp->core.registers[DSP_REG_SSL] = 0;
dsp->core.stack[1][sp_value] = 0;
} else {
dsp->core.registers[DSP_REG_SSL] = value & BITMASK(16);
dsp->core.stack[1][sp_value] = value & BITMASK(16);
}
return true;
}
}
}
/* ...then registers where address & mask are enough */
bits = dsp_get_register_address(dsp, arg, &addr, &mask);
switch (bits) {
case 32:
*addr = value & mask;
return true;
case 16:
*(uint16_t*)addr = value & mask;
return true;
}
return false;
}
#endif

37
hw/xbox/mcpx/dsp/debug.h Normal file
View File

@ -0,0 +1,37 @@
/*
* MCPX DSP emulator
* Copyright (c) 2025 Matt Borgerson
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef HW_XBOX_MCPX_DSP_DEBUG_H
#define HW_XBOX_MCPX_DSP_DEBUG_H
#ifndef DEBUG_DSP
#define DEBUG_DSP 0
#endif
#define TRACE_DSP_DISASM 0
#define TRACE_DSP_DISASM_REG 0
#define TRACE_DSP_DISASM_MEM 0
#define DPRINTF(fmt, ...) \
do { \
if (DEBUG_DSP) fprintf(stderr, fmt, ## __VA_ARGS__); \
} while (0)
#endif

View File

@ -2,6 +2,7 @@
* MCPX DSP emulator
*
* Copyright (c) 2015 espes
* Copyright (c) 2020-2025 Matt Borgerson
*
* Adapted from Hatari DSP M56001 emulation
* (C) 2001-2008 ARAnyM developer team
@ -23,17 +24,12 @@
*/
#include "qemu/osdep.h"
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include "dsp_cpu.h"
#include "dsp_dma.h"
#include "dsp_state.h"
#include "dsp.h"
#include "debug.h"
#include "trace.h"
/* Defines */
#define BITMASK(x) ((1<<(x))-1)
@ -42,16 +38,6 @@
#define INTERRUPT_START_FRAME (1 << 1)
#define INTERRUPT_DMA_EOL (1 << 7)
// #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);
@ -91,8 +77,6 @@ void dsp_destroy(DSPState* dsp)
static uint32_t read_peripheral(dsp_core_t* core, uint32_t address) {
DSPState* dsp = container_of(core, DSPState, core);
DPRINTF("read_peripheral 0x%06x", address);
uint32_t v = 0xababa;
switch(address) {
case 0xFFFFB3:
@ -118,15 +102,13 @@ static uint32_t read_peripheral(dsp_core_t* core, uint32_t address) {
break;
}
DPRINTF(" -> 0x%06x\n", v);
trace_dsp_read_peripheral(address, v);
return v;
}
static void write_peripheral(dsp_core_t* core, uint32_t address, uint32_t value) {
DSPState* dsp = container_of(core, DSPState, core);
DPRINTF("write_peripheral [0x%06x] = 0x%06x\n", address, value);
switch(address) {
case 0xFFFFC4:
if (value & 1) {
@ -152,6 +134,8 @@ static void write_peripheral(dsp_core_t* core, uint32_t address, uint32_t value)
dsp_dma_write(&dsp->dma, DMA_CONFIGURATION, value);
break;
}
trace_dsp_write_peripheral(address, value);
}
@ -201,7 +185,7 @@ void dsp_bootstrap(DSPState* dsp)
(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]);
DPRINTF("Bootstrap %04x: %08x\n", i, dsp->core.pram[i]);
dsp->core.pram[i] &= 0x00ffffff;
}
}
@ -213,19 +197,6 @@ void dsp_start_frame(DSPState* dsp)
dsp->interrupts |= INTERRUPT_START_FRAME;
}
/**
* Disassemble DSP code between given addresses, return next PC address
*/
uint32_t dsp_disasm_address(DSPState* dsp, FILE *out, uint32_t lowerAdr, uint32_t UpperAdr)
{
uint32_t dsp_pc;
for (dsp_pc=lowerAdr; dsp_pc<=UpperAdr; dsp_pc++) {
dsp_pc += dsp56k_execute_one_disasm_instruction(&dsp->core, out, dsp_pc);
}
return dsp_pc;
}
uint32_t dsp_read_memory(DSPState* dsp, char space, uint32_t address)
{
int space_id;
@ -269,259 +240,3 @@ void dsp_write_memory(DSPState* dsp, char space, uint32_t address, uint32_t valu
dsp56k_write_memory(&dsp->core, space_id, address, value);
}
/**
* Output memory values between given addresses in given DSP address space.
* Return next DSP address value.
*/
uint32_t dsp_disasm_memory(DSPState* dsp, uint32_t dsp_memdump_addr, uint32_t dsp_memdump_upper, char space)
{
uint32_t mem, value;
for (mem = dsp_memdump_addr; mem <= dsp_memdump_upper; mem++) {
value = dsp_read_memory(dsp, space, mem);
printf("%04x %06x\n", mem, value);
}
return dsp_memdump_upper+1;
}
/**
* Show information on DSP core state which isn't
* shown by any of the other commands (dd, dm, dr).
*/
void dsp_info(DSPState* dsp)
{
int i, j;
const char *stackname[] = { "SSH", "SSL" };
printf("DSP core information:\n");
for (i = 0; i < ARRAY_SIZE(stackname); i++) {
printf("- %s stack:", stackname[i]);
for (j = 0; j < ARRAY_SIZE(dsp->core.stack[0]); j++) {
printf(" %04x", dsp->core.stack[i][j]);
}
printf("\n");
}
printf("- Interrupt IPL:");
for (i = 0; i < ARRAY_SIZE(dsp->core.interrupt_ipl); i++) {
printf(" %04x", dsp->core.interrupt_ipl[i]);
}
printf("\n");
printf("- Pending ints: ");
for (i = 0; i < ARRAY_SIZE(dsp->core.interrupt_is_pending); i++) {
printf(" %04hx", dsp->core.interrupt_is_pending[i]);
}
printf("\n");
}
/**
* Show DSP register contents
*/
void dsp_print_registers(DSPState* dsp)
{
uint32_t i;
printf("A: A2: %02x A1: %06x A0: %06x\n",
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",
i, dsp->core.registers[DSP_REG_R0+i],
i, dsp->core.registers[DSP_REG_N0+i],
i, dsp->core.registers[DSP_REG_M0+i]);
}
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",
dsp->core.registers[DSP_REG_SP], dsp->core.registers[DSP_REG_SSH], dsp->core.registers[DSP_REG_SSL]);
}
/**
* Get given DSP register address and required bit mask.
* Works for A0-2, B0-2, LA, LC, M0-7, N0-7, R0-7, X0-1, Y0-1, PC, SR, SP,
* OMR, SSH & SSL registers, but note that the SP, SSH & SSL registers
* need special handling (in DSP*SetRegister()) when they are set.
* Return the register width in bits or zero for an error.
*/
int dsp_get_register_address(DSPState* dsp, const char *regname, uint32_t **addr, uint32_t *mask)
{
#define MAX_REGNAME_LEN 4
typedef struct {
const char name[MAX_REGNAME_LEN];
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[] = {
/* 56-bit A register */
{ "A0", &dsp->core.registers[DSP_REG_A0], 32, BITMASK(24) },
{ "A1", &dsp->core.registers[DSP_REG_A1], 32, BITMASK(24) },
{ "A2", &dsp->core.registers[DSP_REG_A2], 32, BITMASK(8) },
/* 56-bit B register */
{ "B0", &dsp->core.registers[DSP_REG_B0], 32, BITMASK(24) },
{ "B1", &dsp->core.registers[DSP_REG_B1], 32, BITMASK(24) },
{ "B2", &dsp->core.registers[DSP_REG_B2], 32, BITMASK(8) },
/* 16-bit LA & LC registers */
{ "LA", &dsp->core.registers[DSP_REG_LA], 32, BITMASK(16) },
{ "LC", &dsp->core.registers[DSP_REG_LC], 32, BITMASK(16) },
/* 16-bit M registers */
{ "M0", &dsp->core.registers[DSP_REG_M0], 32, BITMASK(16) },
{ "M1", &dsp->core.registers[DSP_REG_M1], 32, BITMASK(16) },
{ "M2", &dsp->core.registers[DSP_REG_M2], 32, BITMASK(16) },
{ "M3", &dsp->core.registers[DSP_REG_M3], 32, BITMASK(16) },
{ "M4", &dsp->core.registers[DSP_REG_M4], 32, BITMASK(16) },
{ "M5", &dsp->core.registers[DSP_REG_M5], 32, BITMASK(16) },
{ "M6", &dsp->core.registers[DSP_REG_M6], 32, BITMASK(16) },
{ "M7", &dsp->core.registers[DSP_REG_M7], 32, BITMASK(16) },
/* 16-bit N registers */
{ "N0", &dsp->core.registers[DSP_REG_N0], 32, BITMASK(16) },
{ "N1", &dsp->core.registers[DSP_REG_N1], 32, BITMASK(16) },
{ "N2", &dsp->core.registers[DSP_REG_N2], 32, BITMASK(16) },
{ "N3", &dsp->core.registers[DSP_REG_N3], 32, BITMASK(16) },
{ "N4", &dsp->core.registers[DSP_REG_N4], 32, BITMASK(16) },
{ "N5", &dsp->core.registers[DSP_REG_N5], 32, BITMASK(16) },
{ "N6", &dsp->core.registers[DSP_REG_N6], 32, BITMASK(16) },
{ "N7", &dsp->core.registers[DSP_REG_N7], 32, BITMASK(16) },
{ "OMR", &dsp->core.registers[DSP_REG_OMR], 32, 0x5f },
/* 16-bit program counter */
{ "PC", (uint32_t*)(&dsp->core.pc), 24, BITMASK(24) },
/* 16-bit DSP R (address) registers */
{ "R0", &dsp->core.registers[DSP_REG_R0], 32, BITMASK(16) },
{ "R1", &dsp->core.registers[DSP_REG_R1], 32, BITMASK(16) },
{ "R2", &dsp->core.registers[DSP_REG_R2], 32, BITMASK(16) },
{ "R3", &dsp->core.registers[DSP_REG_R3], 32, BITMASK(16) },
{ "R4", &dsp->core.registers[DSP_REG_R4], 32, BITMASK(16) },
{ "R5", &dsp->core.registers[DSP_REG_R5], 32, BITMASK(16) },
{ "R6", &dsp->core.registers[DSP_REG_R6], 32, BITMASK(16) },
{ "R7", &dsp->core.registers[DSP_REG_R7], 32, BITMASK(16) },
{ "SSH", &dsp->core.registers[DSP_REG_SSH], 32, BITMASK(16) },
{ "SSL", &dsp->core.registers[DSP_REG_SSL], 32, BITMASK(16) },
{ "SP", &dsp->core.registers[DSP_REG_SP], 32, BITMASK(6) },
/* 16-bit status register */
{ "SR", &dsp->core.registers[DSP_REG_SR], 32, 0xefff },
/* 48-bit X register */
{ "X0", &dsp->core.registers[DSP_REG_X0], 32, BITMASK(24) },
{ "X1", &dsp->core.registers[DSP_REG_X1], 32, BITMASK(24) },
/* 48-bit Y register */
{ "Y0", &dsp->core.registers[DSP_REG_Y0], 32, BITMASK(24) },
{ "Y1", &dsp->core.registers[DSP_REG_Y1], 32, BITMASK(24) }
};
/* left, right, middle, direction */
int l, r, m, dir = 0;
unsigned int i, len;
char reg[MAX_REGNAME_LEN];
for (i = 0; i < sizeof(reg) && regname[i]; i++) {
reg[i] = toupper(regname[i]);
}
if (i < 2 || regname[i]) {
/* too short or longer than any of the names */
return 0;
}
len = i;
/* bisect */
l = 0;
r = ARRAY_SIZE(registers) - 1;
do {
m = (l+r) >> 1;
for (i = 0; i < len; i++) {
dir = (int)reg[i] - registers[m].name[i];
if (dir) {
break;
}
}
if (dir == 0) {
*addr = registers[m].addr;
*mask = registers[m].mask;
return registers[m].bits;
}
if (dir < 0) {
r = m-1;
} else {
l = m+1;
}
} while (l <= r);
#undef MAX_REGNAME_LEN
return 0;
}
/**
* Set given DSP register value, return false if unknown register given
*/
bool dsp_disasm_set_register(DSPState* dsp, const char *arg, uint32_t value)
{
uint32_t *addr, mask, sp_value;
int bits;
/* first check registers needing special handling... */
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);
dsp->core.registers[DSP_REG_SSH] = dsp->core.stack[0][value];
dsp->core.registers[DSP_REG_SSL] = dsp->core.stack[1][value];
return true;
}
if (arg[1]=='S' || arg[1]=='s') {
sp_value = dsp->core.registers[DSP_REG_SP] & BITMASK(4);
if (arg[2]=='H' || arg[2]=='h') {
if (sp_value == 0) {
dsp->core.registers[DSP_REG_SSH] = 0;
dsp->core.stack[0][sp_value] = 0;
} else {
dsp->core.registers[DSP_REG_SSH] = value & BITMASK(16);
dsp->core.stack[0][sp_value] = value & BITMASK(16);
}
return true;
}
if (arg[2]=='L' || arg[2]=='l') {
if (sp_value == 0) {
dsp->core.registers[DSP_REG_SSL] = 0;
dsp->core.stack[1][sp_value] = 0;
} else {
dsp->core.registers[DSP_REG_SSL] = value & BITMASK(16);
dsp->core.stack[1][sp_value] = value & BITMASK(16);
}
return true;
}
}
}
/* ...then registers where address & mask are enough */
bits = dsp_get_register_address(dsp, arg, &addr, &mask);
switch (bits) {
case 32:
*addr = value & mask;
return true;
case 16:
*(uint16_t*)addr = value & mask;
return true;
}
return false;
}

View File

@ -36,7 +36,6 @@ typedef void (*dsp_scratch_rw_func)(
typedef void (*dsp_fifo_rw_func)(
void *opaque, uint8_t *ptr, unsigned int index, size_t len, bool dir);
/* Dsp commands */
DSPState *dsp_init(void *rw_opaque,
dsp_scratch_rw_func scratch_rw,
dsp_fifo_rw_func fifo_rw);
@ -49,16 +48,14 @@ void dsp_run(DSPState* dsp, int cycles);
void dsp_bootstrap(DSPState* dsp);
void dsp_start_frame(DSPState* dsp);
/* Dsp Debugger commands */
uint32_t dsp_read_memory(DSPState* dsp, char space, uint32_t addr);
void dsp_write_memory(DSPState* dsp, char space, uint32_t address, uint32_t value);
uint32_t dsp_disasm_memory(DSPState* dsp, uint32_t dsp_memdump_addr, uint32_t dsp_memdump_upper, char space);
uint32_t dsp_disasm_address(DSPState* dsp, FILE *out, uint32_t lowerAdr, uint32_t UpperAdr);
void dsp_info(DSPState* dsp);
void dsp_print_registers(DSPState* dsp);
int dsp_get_register_address(DSPState* dsp, const char *arg, uint32_t **addr, uint32_t *mask);
uint32_t dsp_disasm_memory(DSPState* dsp, uint32_t dsp_memdump_addr, uint32_t dsp_memdump_upper, char space);
uint32_t dsp_disasm_address(DSPState* dsp, FILE *out, uint32_t lowerAdr, uint32_t UpperAdr);
bool dsp_disasm_set_register(DSPState* dsp, const char *arg, uint32_t value);
#endif /* DSP_H */

View File

@ -2,6 +2,7 @@
* DSP56300 emulator
*
* Copyright (c) 2015 espes
* Copyright (c) 2020-2025 Matt Borgerson
*
* Adapted from Hatari DSP M56001 emulation
* (C) 2003-2008 ARAnyM developer team
@ -23,20 +24,10 @@
*/
#include "qemu/osdep.h"
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#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
#define DPRINTF(s, ...) printf(s, ## __VA_ARGS__)
#include "debug.h"
#include "trace.h"
#define BITMASK(x) ((1<<(x))-1)
@ -80,18 +71,18 @@ static void dsp_rnd56(dsp_core_t* dsp, uint32_t *dest);
static uint32_t dsp_signextend(int bits, uint32_t v);
static const dsp_interrupt_t dsp_interrupt[12] = {
{DSP_INTER_RESET , 0x00, 0, "Reset"},
{DSP_INTER_ILLEGAL , 0x3e, 0, "Illegal"},
{DSP_INTER_STACK_ERROR , 0x02, 0, "Stack Error"},
{DSP_INTER_TRACE , 0x04, 0, "Trace"},
{DSP_INTER_SWI , 0x06, 0, "Swi"},
{DSP_INTER_HOST_COMMAND , 0xff, 1, "Host Command"},
{DSP_INTER_HOST_RCV_DATA, 0x20, 1, "Host receive"},
{DSP_INTER_HOST_TRX_DATA, 0x22, 1, "Host transmit"},
{DSP_INTER_SSI_RCV_DATA_E, 0x0e, 2, "SSI receive with exception"},
{DSP_INTER_SSI_RCV_DATA , 0x0c, 2, "SSI receive"},
{DSP_INTER_SSI_TRX_DATA_E, 0x12, 2, "SSI transmit with exception"},
{DSP_INTER_SSI_TRX_DATA , 0x10, 2, "SSI tramsmit"}
{ DSP_INTER_RESET, 0x00, 0, "Reset" },
{ DSP_INTER_ILLEGAL, 0x3e, 0, "Illegal" },
{ DSP_INTER_STACK_ERROR, 0x02, 0, "Stack Error" },
{ DSP_INTER_TRACE, 0x04, 0, "Trace" },
{ DSP_INTER_SWI, 0x06, 0, "Swi" },
{ DSP_INTER_HOST_COMMAND, 0xff, 1, "Host Command" },
{ DSP_INTER_HOST_RCV_DATA, 0x20, 1, "Host receive" },
{ DSP_INTER_HOST_TRX_DATA, 0x22, 1, "Host transmit" },
{ DSP_INTER_SSI_RCV_DATA_E, 0x0e, 2, "SSI receive with exception" },
{ DSP_INTER_SSI_RCV_DATA, 0x0c, 2, "SSI receive" },
{ DSP_INTER_SSI_TRX_DATA_E, 0x12, 2, "SSI transmit with exception" },
{ DSP_INTER_SSI_TRX_DATA, 0x10, 2, "SSI transmit" }
};
static const int registers_tcc[16][2] = {
@ -138,9 +129,8 @@ static const int registers_mask[64] = {
16, 16, 16, 16
};
#include "dsp_emu.inl"
#include "dsp_dis.inl"
#include "dsp_emu.c.inc"
#include "dsp_dis.c.inc"
typedef bool (*match_func_t)(uint32_t op);
@ -415,7 +405,6 @@ void dsp56k_reset_cpu(dsp_core_t* dsp)
/* runtime shit */
dsp->executing_for_disasm = false;
// start_time = SDL_GetTicks();
dsp->num_inst = 0;
@ -462,7 +451,7 @@ static uint16_t disasm_instruction(dsp_core_t* dsp, dsp_trace_disasm_t mode)
if (mode == DSP_TRACE_MODE) {
if (dsp->disasm_prev_inst_pc == dsp->pc) {
if (!dsp->disasm_is_looping) {
printf( "Looping on DSP instruction at PC = $%04x\n", dsp->disasm_prev_inst_pc);
DPRINTF("Looping on DSP instruction at PC = $%04x\n", dsp->disasm_prev_inst_pc);
dsp->disasm_is_looping = true;
}
return 0;
@ -520,7 +509,7 @@ static void disasm_reg_compare(dsp_core_t* dsp)
case DSP_REG_X1:
case DSP_REG_Y0:
case DSP_REG_Y1:
printf("\tReg: %s $%06x -> $%06x\n",
DPRINTF("\tReg: %s $%06x -> $%06x\n",
registers_name[i], dsp->disasm_registers_save[i], dsp->registers[i]);
break;
case DSP_REG_R0:
@ -550,21 +539,21 @@ static void disasm_reg_compare(dsp_core_t* dsp)
case DSP_REG_SR:
case DSP_REG_LA:
case DSP_REG_LC:
printf("\tReg: %s $%04x -> $%04x\n",
DPRINTF("\tReg: %s $%04x -> $%04x\n",
registers_name[i], dsp->disasm_registers_save[i], dsp->registers[i]);
break;
case DSP_REG_OMR:
case DSP_REG_SP:
case DSP_REG_SSH:
case DSP_REG_SSL:
printf("\tReg: %s $%02x -> $%02x\n",
DPRINTF("\tReg: %s $%02x -> $%02x\n",
registers_name[i], dsp->disasm_registers_save[i], dsp->registers[i]);
break;
case DSP_REG_A0:
case DSP_REG_A1:
case DSP_REG_A2:
if (bRegA == false) {
printf("\tReg: a $%02x:%06x:%06x -> $%02x:%06x:%06x\n",
DPRINTF("\tReg: a $%02x:%06x:%06x -> $%02x:%06x:%06x\n",
dsp->disasm_registers_save[DSP_REG_A2], dsp->disasm_registers_save[DSP_REG_A1], dsp->disasm_registers_save[DSP_REG_A0],
dsp->registers[DSP_REG_A2], dsp->registers[DSP_REG_A1], dsp->registers[DSP_REG_A0]
);
@ -575,7 +564,7 @@ static void disasm_reg_compare(dsp_core_t* dsp)
case DSP_REG_B1:
case DSP_REG_B2:
if (bRegB == false) {
printf("\tReg: b $%02x:%06x:%06x -> $%02x:%06x:%06x\n",
DPRINTF("\tReg: b $%02x:%06x:%06x -> $%02x:%06x:%06x\n",
dsp->disasm_registers_save[DSP_REG_B2], dsp->disasm_registers_save[DSP_REG_B1], dsp->disasm_registers_save[DSP_REG_B0],
dsp->registers[DSP_REG_B2], dsp->registers[DSP_REG_B1], dsp->registers[DSP_REG_B0]
);
@ -587,70 +576,28 @@ static void disasm_reg_compare(dsp_core_t* dsp)
#ifdef DSP_DISASM_REG_PC
if (pc_save != dsp->pc) {
printf("\tReg: pc $%04x -> $%04x\n", pc_save, dsp->pc);
DPRINTF("\tReg: pc $%04x -> $%04x\n", pc_save, dsp->pc);
}
#endif
}
static const char* disasm_get_instruction_text(dsp_core_t* dsp)
{
// const int len = sizeof(dsp->disasm_str_instr);
// uint64_t count, cycles;
// uint16_t cycle_diff;
// float percentage;
// int offset;
if (dsp->disasm_is_looping) {
dsp->disasm_str_instr2[0] = 0;
}
// if (dsp->disasm_cur_inst_len == 1) {
// offset = sprintf(dsp->disasm_str_instr2, "p:%04x %06x (%02d cyc) %-*s\n", dsp->disasm_prev_inst_pc, dsp->disasm_cur_inst, dsp->instr_cycle, len, dsp->disasm_str_instr);
// } else {
// offset = sprintf(dsp->disasm_str_instr2, "p:%04x %06x %06x (%02d cyc) %-*s\n", dsp->disasm_prev_inst_pc, dsp->disasm_cur_inst, read_memory_p(dsp, dsp->disasm_prev_inst_pc + 1), dsp->instr_cycle, len, dsp->disasm_str_instr);
// }
// if (offset > 2 && Profile_DspAddressData(dsp->disasm_prev_inst_pc, &percentage, &count, &cycles, &cycle_diff)) {
// offset -= 2;
// sprintf(str_instr2+offset, "%5.2f%% (%"PRId64", %"PRId64", %d)\n",
// percentage, count, cycles, cycle_diff);
// }
if (dsp->disasm_cur_inst_len == 1) {
snprintf(dsp->disasm_str_instr2, sizeof(dsp->disasm_str_instr2), "p:%04x %06x (%02d cyc) %s", dsp->disasm_prev_inst_pc, dsp->disasm_cur_inst, dsp->instr_cycle, dsp->disasm_str_instr);
} else {
snprintf(dsp->disasm_str_instr2, sizeof(dsp->disasm_str_instr2), "p:%04x %06x %06x (%02d cyc) %s", dsp->disasm_prev_inst_pc, dsp->disasm_cur_inst, read_memory_p(dsp, dsp->disasm_prev_inst_pc + 1), dsp->instr_cycle, dsp->disasm_str_instr);
}
return dsp->disasm_str_instr2;
}
/**
* Execute one instruction in trace mode at a given PC address.
* */
uint16_t dsp56k_execute_one_disasm_instruction(dsp_core_t* dsp, FILE *out, uint32_t pc)
{
dsp_core_t dsp_core_save;
/* Set DSP in disasm mode */
dsp->executing_for_disasm = true;
/* Save DSP context before executing instruction */
memcpy(&dsp_core_save, dsp, sizeof(dsp_core_t));
/* execute and disasm instruction */
dsp->pc = pc;
/* Disasm instruction */
uint16_t instruction_length = disasm_instruction(dsp, DSP_DISASM_MODE) - 1;
/* Execute instruction at address given in parameter to get the number of cycles it takes */
dsp56k_execute_instruction(dsp);
fprintf(out, "%s", disasm_get_instruction_text(dsp));
/* 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;
return instruction_length;
}
void dsp56k_execute_instruction(dsp_core_t* dsp)
{
trace_dsp56k_execute_instruction(dsp->is_gp, dsp->pc);
uint32_t disasm_return = 0;
dsp->disasm_memory_ptr = 0;
@ -661,17 +608,18 @@ void dsp56k_execute_instruction(dsp_core_t* dsp)
dsp->cur_inst_len = 1;
dsp->instr_cycle = 2;
/* Disasm current instruction ? (trace mode only) */
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);
bool tracing = TRACE_DSP_DISASM || trace_event_get_state(TRACE_DSP56K_EXECUTE_INSTRUCTION_DISASM);
if (disasm_return) {
printf( "%s", disasm_get_instruction_text(dsp));
/* Disasm current instruction ? (trace mode only) */
if (tracing) {
disasm_return = disasm_instruction(dsp, DSP_TRACE_MODE);
if (disasm_return) {
const char *text = disasm_get_instruction_text(dsp);
trace_dsp56k_execute_instruction_disasm(text);
if (TRACE_DSP_DISASM) {
DPRINTF("%s\n", text);
}
if (disasm_return != 0 && TRACE_DSP_DISASM_REG) {
/* DSP regs trace enabled only if DSP DISASM is enabled */
if (TRACE_DSP_DISASM_REG) {
disasm_reg_save(dsp);
}
}
@ -686,7 +634,7 @@ void dsp56k_execute_instruction(dsp_core_t* dsp)
if (op->emu_func) {
op->emu_func(dsp);
} else {
printf("%x - %s\n", dsp->cur_inst, op->name);
DPRINTF("%x - %s\n", dsp->cur_inst, op->name);
emu_undefined(dsp);
}
} else {
@ -695,26 +643,18 @@ void dsp56k_execute_instruction(dsp_core_t* dsp)
}
/* Disasm current instruction ? (trace mode only) */
if (TRACE_DSP_DISASM) {
/* Display only when DSP is called in trace mode */
if (!dsp->executing_for_disasm) {
if (disasm_return != 0) {
// printf( "%s", disasm_get_instruction_text(dsp));
/* DSP regs trace enabled only if DSP DISASM is enabled */
if (TRACE_DSP_DISASM_REG)
disasm_reg_compare(dsp);
if (TRACE_DSP_DISASM_MEM) {
/* 1 memory change to display ? */
if (dsp->disasm_memory_ptr == 1)
printf( "\t%s\n", dsp->str_disasm_memory[0]);
/* 2 memory changes to display ? */
else if (dsp->disasm_memory_ptr == 2) {
printf( "\t%s\n", dsp->str_disasm_memory[0]);
printf( "\t%s\n", dsp->str_disasm_memory[1]);
}
}
if (tracing && disasm_return) {
if (TRACE_DSP_DISASM_REG) {
disasm_reg_compare(dsp);
}
if (TRACE_DSP_DISASM_MEM) {
/* 1 memory change to display ? */
if (dsp->disasm_memory_ptr == 1)
DPRINTF("\t%s\n", dsp->str_disasm_memory[0]);
/* 2 memory changes to display ? */
else if (dsp->disasm_memory_ptr == 2) {
DPRINTF("\t%s\n", dsp->str_disasm_memory[0]);
DPRINTF("\t%s\n", dsp->str_disasm_memory[1]);
}
}
}
@ -734,7 +674,7 @@ void dsp56k_execute_instruction(dsp_core_t* dsp)
/* Evaluate time after <N> instructions have been executed to avoid asking too frequently */
uint32_t cur_time = SDL_GetTicks();
if (cur_time-start_time>1000) {
printf( "Dsp: %d i/s\n", (dsp->num_inst*1000)/(cur_time-start_time));
DPRINTF("Dsp: %d i/s\n", (dsp->num_inst*1000)/(cur_time-start_time));
start_time=cur_time;
dsp->num_inst=0;
}
@ -1021,9 +961,6 @@ uint32_t dsp56k_read_memory(dsp_core_t* dsp, int space, uint32_t address)
void dsp56k_write_memory(dsp_core_t* dsp, int space, uint32_t address, uint32_t value)
{
assert((value & 0xFF000000) == 0);
assert((address & 0xFF000000) == 0);
if (TRACE_DSP_DISASM_MEM)
write_memory_disasm(dsp, space, address, value);
else
@ -1122,9 +1059,7 @@ static void dsp_write_reg(dsp_core_t* dsp, uint32_t numreg, uint32_t value)
/* Stack underflow or overflow detected, raise interrupt */
dsp56k_add_interrupt(dsp, DSP_INTER_STACK_ERROR);
dsp->registers[DSP_REG_SP] = value & (3<<DSP_SP_SE);
if (!dsp->executing_for_disasm) {
printf( "Dsp: Stack Overflow or Underflow\n");
}
DPRINTF("Dsp: Stack Overflow or Underflow\n");
if (dsp->exception_debugging) {
assert(false);
}
@ -1167,8 +1102,7 @@ static void dsp_stack_push(dsp_core_t* dsp, uint32_t curpc, uint32_t cursr, uint
if ((stack_error==0) && (stack & (1<<DSP_SP_SE))) {
/* Stack full, raise interrupt */
dsp56k_add_interrupt(dsp, DSP_INTER_STACK_ERROR);
if (!dsp->executing_for_disasm)
printf("Dsp: Stack Overflow\n");
DPRINTF("Dsp: Stack Overflow\n");
if (dsp->exception_debugging)
assert(false);
}
@ -1204,8 +1138,7 @@ static void dsp_stack_pop(dsp_core_t* dsp, uint32_t *newpc, uint32_t *newsr)
if ((stack_error==0) && (stack & (1<<DSP_SP_SE))) {
/* Stack empty*/
dsp56k_add_interrupt(dsp, DSP_INTER_STACK_ERROR);
if (!dsp->executing_for_disasm)
printf("Dsp: Stack underflow\n");
DPRINTF("Dsp: Stack underflow\n");
if (dsp->exception_debugging)
assert(false);
}
@ -1458,5 +1391,3 @@ static uint32_t dsp_signextend(int bits, uint32_t v) {
assert(shift > 0);
return (uint32_t)(((int32_t)v << shift) >> shift);
}

View File

@ -29,115 +29,7 @@
#include <stdint.h>
#include <stdbool.h>
#define DSP_OMR_MA 0x00
#define DSP_OMR_MB 0x01
#define DSP_OMR_DE 0x02
#define DSP_OMR_SD 0x06
#define DSP_OMR_EA 0x07
#define DSP_SR_C 0x00
#define DSP_SR_V 0x01
#define DSP_SR_Z 0x02
#define DSP_SR_N 0x03
#define DSP_SR_U 0x04
#define DSP_SR_E 0x05
#define DSP_SR_L 0x06
#define DSP_SR_I0 0x08
#define DSP_SR_I1 0x09
#define DSP_SR_S0 0x0a
#define DSP_SR_S1 0x0b
#define DSP_SR_T 0x0d
#define DSP_SR_LF 0x0f
#define DSP_SP_SE 0x04
#define DSP_SP_UF 0x05
/* Registers numbers in dsp.registers[] */
#define DSP_REG_X0 0x04
#define DSP_REG_X1 0x05
#define DSP_REG_Y0 0x06
#define DSP_REG_Y1 0x07
#define DSP_REG_A0 0x08
#define DSP_REG_B0 0x09
#define DSP_REG_A2 0x0a
#define DSP_REG_B2 0x0b
#define DSP_REG_A1 0x0c
#define DSP_REG_B1 0x0d
#define DSP_REG_A 0x0e
#define DSP_REG_B 0x0f
#define DSP_REG_R0 0x10
#define DSP_REG_R1 0x11
#define DSP_REG_R2 0x12
#define DSP_REG_R3 0x13
#define DSP_REG_R4 0x14
#define DSP_REG_R5 0x15
#define DSP_REG_R6 0x16
#define DSP_REG_R7 0x17
#define DSP_REG_N0 0x18
#define DSP_REG_N1 0x19
#define DSP_REG_N2 0x1a
#define DSP_REG_N3 0x1b
#define DSP_REG_N4 0x1c
#define DSP_REG_N5 0x1d
#define DSP_REG_N6 0x1e
#define DSP_REG_N7 0x1f
#define DSP_REG_M0 0x20
#define DSP_REG_M1 0x21
#define DSP_REG_M2 0x22
#define DSP_REG_M3 0x23
#define DSP_REG_M4 0x24
#define DSP_REG_M5 0x25
#define DSP_REG_M6 0x26
#define DSP_REG_M7 0x27
#define DSP_REG_SR 0x39
#define DSP_REG_OMR 0x3a
#define DSP_REG_SP 0x3b
#define DSP_REG_SSH 0x3c
#define DSP_REG_SSL 0x3d
#define DSP_REG_LA 0x3e
#define DSP_REG_LC 0x3f
#define DSP_REG_NULL 0x00
#define DSP_REG_LCSAVE 0x30
#define DSP_REG_MAX 0x40
/* Memory spaces for dsp.ram[], dsp.rom[] */
#define DSP_SPACE_X 0x00
#define DSP_SPACE_Y 0x01
#define DSP_SPACE_P 0x02
#define DSP_XRAM_SIZE 4096
#define DSP_YRAM_SIZE 2048
#define DSP_PRAM_SIZE 4096
#define DSP_MIXBUFFER_BASE 0x001400
#define DSP_MIXBUFFER_SIZE 1024
#define DSP_PERIPH_BASE 0xFFFF80
#define DSP_PERIPH_SIZE 128
#define DSP_INTERRUPT_NONE 0x0
#define DSP_INTERRUPT_DISABLED 0x1
#define DSP_INTERRUPT_LONG 0x2
#define DSP_INTER_RESET 0x0
#define DSP_INTER_ILLEGAL 0x1
#define DSP_INTER_STACK_ERROR 0x2
#define DSP_INTER_TRACE 0x3
#define DSP_INTER_SWI 0x4
#define DSP_INTER_HOST_COMMAND 0x5
#define DSP_INTER_HOST_RCV_DATA 0x6
#define DSP_INTER_HOST_TRX_DATA 0x7
#define DSP_INTER_SSI_RCV_DATA_E 0x8
#define DSP_INTER_SSI_RCV_DATA 0x9
#define DSP_INTER_SSI_TRX_DATA_E 0xa
#define DSP_INTER_SSI_TRX_DATA 0xb
#include "dsp_cpu_regs.h"
typedef enum {
DSP_TRACE_MODE,
@ -209,10 +101,6 @@ struct dsp_core_s {
/* Current instruction */
uint32_t cur_inst;
/* DSP is in disasm mode ? */
/* If yes, stack overflow, underflow and illegal instructions messages are not displayed */
bool executing_for_disasm;
char str_disasm_memory[2][50]; /* Buffer for memory change text in disasm mode */
uint32_t disasm_memory_ptr; /* Pointer for memory change in disasm mode */
@ -250,7 +138,6 @@ struct dsp_core_s {
/* Functions */
void dsp56k_reset_cpu(dsp_core_t* dsp); /* Set dsp_core to use */
void dsp56k_execute_instruction(dsp_core_t* dsp); /* Execute 1 instruction */
uint16_t dsp56k_execute_one_disasm_instruction(dsp_core_t* dsp, FILE *out, uint32_t pc); /* Execute 1 instruction in disasm mode */
uint32_t dsp56k_read_memory(dsp_core_t* dsp, int space, uint32_t address);
void dsp56k_write_memory(dsp_core_t* dsp, int space, uint32_t address, uint32_t value);

View File

@ -0,0 +1,138 @@
/*
* DSP56300 emulator
*
* Copyright (c) 2015 espes
*
* Adapted from Hatari DSP M56001 emulation
* (C) 2003-2008 ARAnyM developer team
* Adaption to Hatari (C) 2008 by Thomas Huth
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef HW_XBOX_MCPX_DSP_DSP_CPU_REGS_H
#define HW_XBOX_MCPX_DSP_DSP_CPU_REGS_H
#define DSP_OMR_MA 0x00
#define DSP_OMR_MB 0x01
#define DSP_OMR_DE 0x02
#define DSP_OMR_SD 0x06
#define DSP_OMR_EA 0x07
#define DSP_SR_C 0x00
#define DSP_SR_V 0x01
#define DSP_SR_Z 0x02
#define DSP_SR_N 0x03
#define DSP_SR_U 0x04
#define DSP_SR_E 0x05
#define DSP_SR_L 0x06
#define DSP_SR_I0 0x08
#define DSP_SR_I1 0x09
#define DSP_SR_S0 0x0a
#define DSP_SR_S1 0x0b
#define DSP_SR_T 0x0d
#define DSP_SR_LF 0x0f
#define DSP_SP_SE 0x04
#define DSP_SP_UF 0x05
/* Registers numbers in dsp.registers[] */
#define DSP_REG_X0 0x04
#define DSP_REG_X1 0x05
#define DSP_REG_Y0 0x06
#define DSP_REG_Y1 0x07
#define DSP_REG_A0 0x08
#define DSP_REG_B0 0x09
#define DSP_REG_A2 0x0a
#define DSP_REG_B2 0x0b
#define DSP_REG_A1 0x0c
#define DSP_REG_B1 0x0d
#define DSP_REG_A 0x0e
#define DSP_REG_B 0x0f
#define DSP_REG_R0 0x10
#define DSP_REG_R1 0x11
#define DSP_REG_R2 0x12
#define DSP_REG_R3 0x13
#define DSP_REG_R4 0x14
#define DSP_REG_R5 0x15
#define DSP_REG_R6 0x16
#define DSP_REG_R7 0x17
#define DSP_REG_N0 0x18
#define DSP_REG_N1 0x19
#define DSP_REG_N2 0x1a
#define DSP_REG_N3 0x1b
#define DSP_REG_N4 0x1c
#define DSP_REG_N5 0x1d
#define DSP_REG_N6 0x1e
#define DSP_REG_N7 0x1f
#define DSP_REG_M0 0x20
#define DSP_REG_M1 0x21
#define DSP_REG_M2 0x22
#define DSP_REG_M3 0x23
#define DSP_REG_M4 0x24
#define DSP_REG_M5 0x25
#define DSP_REG_M6 0x26
#define DSP_REG_M7 0x27
#define DSP_REG_SR 0x39
#define DSP_REG_OMR 0x3a
#define DSP_REG_SP 0x3b
#define DSP_REG_SSH 0x3c
#define DSP_REG_SSL 0x3d
#define DSP_REG_LA 0x3e
#define DSP_REG_LC 0x3f
#define DSP_REG_NULL 0x00
#define DSP_REG_LCSAVE 0x30
#define DSP_REG_MAX 0x40
/* Memory spaces for dsp.ram[], dsp.rom[] */
#define DSP_SPACE_X 0x00
#define DSP_SPACE_Y 0x01
#define DSP_SPACE_P 0x02
#define DSP_XRAM_SIZE 4096
#define DSP_YRAM_SIZE 2048
#define DSP_PRAM_SIZE 4096
#define DSP_MIXBUFFER_BASE 0x001400
#define DSP_MIXBUFFER_SIZE 1024
#define DSP_PERIPH_BASE 0xFFFF80
#define DSP_PERIPH_SIZE 128
#define DSP_INTERRUPT_NONE 0x0
#define DSP_INTERRUPT_DISABLED 0x1
#define DSP_INTERRUPT_LONG 0x2
#define DSP_INTER_RESET 0x0
#define DSP_INTER_ILLEGAL 0x1
#define DSP_INTER_STACK_ERROR 0x2
#define DSP_INTER_TRACE 0x3
#define DSP_INTER_SWI 0x4
#define DSP_INTER_HOST_COMMAND 0x5
#define DSP_INTER_HOST_RCV_DATA 0x6
#define DSP_INTER_HOST_TRX_DATA 0x7
#define DSP_INTER_SSI_RCV_DATA_E 0x8
#define DSP_INTER_SSI_RCV_DATA 0x9
#define DSP_INTER_SSI_TRX_DATA_E 0xa
#define DSP_INTER_SSI_TRX_DATA 0xb
#endif

View File

@ -18,48 +18,14 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include <stddef.h>
#include "qemu/osdep.h"
#include "qemu/compiler.h"
#include "dsp_dma.h"
#include "dsp_dma_regs.h"
#include "dsp_state.h"
#define DMA_CONFIGURATION_AUTOSTART (1 << 0)
#define DMA_CONFIGURATION_AUTOREADY (1 << 1)
#define DMA_CONFIGURATION_IOC_CLEAR (1 << 2)
#define DMA_CONFIGURATION_EOL_CLEAR (1 << 3)
#define DMA_CONFIGURATION_ERR_CLEAR (1 << 4)
#define DMA_CONTROL_ACTION 0x7
#define DMA_CONTROL_ACTION_NOP 0
#define DMA_CONTROL_ACTION_START 1
#define DMA_CONTROL_ACTION_STOP 2
#define DMA_CONTROL_ACTION_FREEZE 3
#define DMA_CONTROL_ACTION_UNFREEZE 4
#define DMA_CONTROL_ACTION_ABORT 5
#define DMA_CONTROL_FROZEN (1 << 3)
#define DMA_CONTROL_RUNNING (1 << 4)
#define DMA_CONTROL_STOPPED (1 << 5)
#define NODE_POINTER_VAL 0x3fff
#define NODE_POINTER_EOL (1 << 14)
#define NODE_CONTROL_DIRECTION (1 << 1)
// #define DEBUG
#ifdef DEBUG
# define DPRINTF(s, ...) printf(s, ## __VA_ARGS__)
#else
# define DPRINTF(s, ...) do { } while (0)
#endif
#ifdef DEBUG
const char *buffer_names[] = {
"fifo0", /* 0x0 */
"fifo1", /* 0x1 */
@ -98,8 +64,6 @@ const char *space_names[] = {
#endif
#define MIN(a,b) (((a)<(b))?(a):(b))
static void scratch_circular_copy(
DSPDMAState *s,
uint32_t scratch_base,
@ -142,8 +106,6 @@ static void dsp_dma_run(DSPDMAState *s)
return;
}
// DSPState *dsp = container_of(s, DSPState, dma);
while (!(s->next_block & NODE_POINTER_EOL)) {
uint32_t addr = s->next_block & NODE_POINTER_VAL;
uint32_t block_addr = 0;

View File

@ -0,0 +1,46 @@
/*
* MCPX DSP DMA
*
* Copyright (c) 2015 espes
* Copyright (c) 2020-2025 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 HW_XBOX_MCPX_DSP_DSP_DMA_REGS_H
#define HW_XBOX_MCPX_DSP_DSP_DMA_REGS_H
#define DMA_CONFIGURATION_AUTOSTART (1 << 0)
#define DMA_CONFIGURATION_AUTOREADY (1 << 1)
#define DMA_CONFIGURATION_IOC_CLEAR (1 << 2)
#define DMA_CONFIGURATION_EOL_CLEAR (1 << 3)
#define DMA_CONFIGURATION_ERR_CLEAR (1 << 4)
#define DMA_CONTROL_ACTION 0x7
#define DMA_CONTROL_ACTION_NOP 0
#define DMA_CONTROL_ACTION_START 1
#define DMA_CONTROL_ACTION_STOP 2
#define DMA_CONTROL_ACTION_FREEZE 3
#define DMA_CONTROL_ACTION_UNFREEZE 4
#define DMA_CONTROL_ACTION_ABORT 5
#define DMA_CONTROL_FROZEN (1 << 3)
#define DMA_CONTROL_RUNNING (1 << 4)
#define DMA_CONTROL_STOPPED (1 << 5)
#define NODE_POINTER_VAL 0x3fff
#define NODE_POINTER_EOL (1 << 14)
#define NODE_CONTROL_DIRECTION (1 << 1)
#endif

View File

@ -27,15 +27,10 @@ typedef void (*emu_func_t)(dsp_core_t* dsp);
static void emu_undefined(dsp_core_t* dsp)
{
if (!dsp->executing_for_disasm) {
dsp->cur_inst_len = 0;
printf("Dsp: 0x%04x: 0x%06x Illegal instruction\n",dsp->pc, dsp->cur_inst);
/* Add some artificial CPU cycles to avoid being stuck in an infinite loop */
dsp->instr_cycle += 100;
} else {
dsp->cur_inst_len = 1;
dsp->instr_cycle = 0;
}
dsp->cur_inst_len = 0;
printf("Dsp: 0x%04x: 0x%06x Illegal instruction\n",dsp->pc, dsp->cur_inst);
/* Add some artificial CPU cycles to avoid being stuck in an infinite loop */
dsp->instr_cycle += 100;
if (dsp->exception_debugging) {
assert(false);
}

View File

@ -0,0 +1,2 @@
libdsp = static_library('dsp', files(['debug.c', 'dsp.c', 'dsp_cpu.c', 'dsp_dma.c']) + genh)
dsp = declare_dependency(objects: libdsp.extract_all_objects(recursive: false))

View File

@ -0,0 +1,9 @@
# See docs/devel/tracing.rst for syntax documentation.
# dsp.c
dsp_read_peripheral(uint32_t addr, uint32_t val) "addr 0x%"PRIx32" val 0x%"PRIx32
dsp_write_peripheral(uint32_t addr, uint32_t val) "addr 0x%"PRIx32" val 0x%"PRIx32
# dsp_cpu.c
dsp56k_execute_instruction(uint32_t id, uint32_t pc) "[gp=%d]: pc=0x%"PRIx32
dsp56k_execute_instruction_disasm(const char *disasm) "%s"

1
hw/xbox/mcpx/dsp/trace.h Normal file
View File

@ -0,0 +1 @@
#include "trace/trace-hw_xbox_mcpx_dsp.h"

View File

@ -1,10 +1,9 @@
subdir('dsp')
mcpx_ss = ss.source_set()
mcpx_ss.add(sdl, libsamplerate, files(
mcpx_ss.add(sdl, libsamplerate, dsp, files(
'apu.c',
'aci.c',
'dsp/dsp.c',
'dsp/dsp_cpu.c',
'dsp/dsp_dma.c',
))
specific_ss.add_all(mcpx_ss)

View File

@ -32,6 +32,8 @@
(v) |= ((__val) << ctz32(__mask)) & (__mask); \
})
// clang-format off
#define NV_NUM_BLOCKS 21
#define NV_PMC 0 /* card master control */
#define NV_PBUS 1 /* bus control */
@ -312,6 +314,7 @@
# define NV_PGRAPH_CSV0_D_SKIN_4 6
#define NV_PGRAPH_CSV0_C 0x00000FB8
# define NV_PGRAPH_CSV0_C_CHEOPS_PROGRAM_START 0x0000FF00
# define NV_PGRAPH_CSV0_C_SPECULAR_ENABLE (1 << 16)
# define NV_PGRAPH_CSV0_C_SPECULAR (3 << 19)
# define NV_PGRAPH_CSV0_C_DIFFUSE (3 << 21)
# define NV_PGRAPH_CSV0_C_AMBIENT (3 << 23)
@ -1013,6 +1016,7 @@
# define NV097_SET_NORMALIZATION_ENABLE 0x000003A4
# define NV097_SET_MATERIAL_EMISSION 0x000003A8
# define NV097_SET_MATERIAL_ALPHA 0x000003B4
# define NV097_SET_SPECULAR_ENABLE 0x000003B8
# define NV097_SET_LIGHT_ENABLE_MASK 0x000003BC
# define NV097_SET_LIGHT_ENABLE_MASK_LIGHT0_OFF 0
# define NV097_SET_LIGHT_ENABLE_MASK_LIGHT0_INFINITE 1
@ -1463,4 +1467,6 @@
#define NV2A_CUBEMAP_FACE_ALIGNMENT 128
// clang-format on
#endif

View File

@ -383,6 +383,11 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
mstring_append(body, " oD0 = diffuse;\n");
mstring_append(body, " oD1 = specular;\n");
}
if (!state->specular_enable) {
mstring_append(body, " oD1 = vec4(0.0, 0.0, 0.0, 1.0);\n");
}
mstring_append(body, " oB0 = backDiffuse;\n");
mstring_append(body, " oB1 = backSpecular;\n");

View File

@ -34,7 +34,6 @@ MString *pgraph_gen_vsh_glsl(const ShaderState *state, bool prefix_outputs)
mstring_append_fmt(output, "#version %d\n\n", state->vulkan ? 450 : 400);
MString *header = mstring_from_str("");
MString *uniforms = mstring_from_str("");
const char *u = state->vulkan ? "" : "uniform "; // FIXME: Remove
@ -243,9 +242,7 @@ MString *pgraph_gen_vsh_glsl(const ShaderState *state, bool prefix_outputs)
/* Set outputs */
mstring_append(body, "\n"
" vtxD0 = clamp(oD0, 0.0, 1.0);\n"
" vtxD1 = clamp(oD1, 0.0, 1.0);\n"
" vtxB0 = clamp(oB0, 0.0, 1.0);\n"
" vtxB1 = clamp(oB1, 0.0, 1.0);\n"
" vtxFog = oFog.x;\n"
" vtxT0 = oT0;\n"
" vtxT1 = oT1;\n"
@ -254,6 +251,18 @@ MString *pgraph_gen_vsh_glsl(const ShaderState *state, bool prefix_outputs)
" gl_PointSize = oPts.x;\n"
);
if (state->specular_enable) {
mstring_append(body,
" vtxD1 = clamp(oD1, 0.0, 1.0);\n"
" vtxB1 = clamp(oB1, 0.0, 1.0);\n"
);
} else {
mstring_append(body,
" vtxD1 = vec4(0.0, 0.0, 0.0, 1.0);\n"
" vtxB1 = vec4(0.0, 0.0, 0.0, 1.0);\n"
);
}
if (state->vulkan) {
mstring_append(body,
" gl_Position = oPos;\n"

View File

@ -78,6 +78,7 @@ DEF_METHOD(NV097, SET_FRONT_FACE)
DEF_METHOD(NV097, SET_NORMALIZATION_ENABLE)
DEF_METHOD_RANGE(NV097, SET_MATERIAL_EMISSION, 3)
DEF_METHOD(NV097, SET_MATERIAL_ALPHA)
DEF_METHOD(NV097, SET_SPECULAR_ENABLE)
DEF_METHOD(NV097, SET_LIGHT_ENABLE_MASK)
DEF_METHOD_CASE_4(NV097, SET_TEXGEN_S, 16)
DEF_METHOD_CASE_4(NV097, SET_TEXGEN_T, 16)

View File

@ -19,7 +19,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "../nv2a_int.h"
#include "hw/xbox/nv2a/nv2a_int.h"
#include "ui/xemu-notifications.h"
#include "ui/xemu-settings.h"
#include "util.h"
@ -431,7 +431,7 @@ unsigned int nv2a_get_surface_scale_factor(void)
#define DEF_METHOD_CASE_4_OFFSET(gclass, name, offset, stride) /* Drop */
#define DEF_METHOD_CASE_4(gclass, name, stride) \
DEF_METHOD_PROTO(gclass, name);
#include "methods.h"
#include "methods.h.inc"
#undef DEF_METHOD
#undef DEF_METHOD_RANGE
#undef DEF_METHOD_CASE_4_OFFSET
@ -485,7 +485,7 @@ static const struct {
},
#define DEF_METHOD_CASE_4(gclass, name, stride) \
DEF_METHOD_CASE_4_OFFSET(gclass, name, 0, stride)
#include "methods.h"
#include "methods.h.inc"
#undef DEF_METHOD
#undef DEF_METHOD_RANGE
#undef DEF_METHOD_CASE_4_OFFSET
@ -504,7 +504,7 @@ static const struct {
#define DEF_METHOD_CASE_4(gclass, name, stride) \
static const size_t METHOD_RANGE_END_NAME(gclass, name) = \
METHOD_ADDR(gclass, name) + 4*stride;
#include "methods.h"
#include "methods.h.inc"
#undef DEF_METHOD
#undef DEF_METHOD_RANGE
#undef DEF_METHOD_CASE_4_OFFSET
@ -1619,6 +1619,11 @@ DEF_METHOD(NV097, SET_MATERIAL_ALPHA)
pg->material_alpha = *(float*)&parameter;
}
DEF_METHOD(NV097, SET_SPECULAR_ENABLE)
{
PG_SET_MASK(NV_PGRAPH_CSV0_C, NV_PGRAPH_CSV0_C_SPECULAR_ENABLE, parameter);
}
DEF_METHOD(NV097, SET_LIGHT_ENABLE_MASK)
{
PG_SET_MASK(NV_PGRAPH_CSV0_D, NV_PGRAPH_CSV0_D_LIGHTS, parameter);
@ -3022,4 +3027,3 @@ void pgraph_pre_shutdown_wait(NV2AState *d)
PGRAPHState *pg = &d->pgraph;
pg->renderer->ops.pre_shutdown_wait(d);
}

View File

@ -17,7 +17,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "../nv2a_int.h"
#include "hw/xbox/nv2a/nv2a_int.h"
NV2AStats g_nv2a_stats;

View File

@ -19,7 +19,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "../nv2a_int.h"
#include "hw/xbox/nv2a/nv2a_int.h"
uint32_t pgraph_rdi_read(PGRAPHState *pg, unsigned int select,
unsigned int address)

View File

@ -69,6 +69,8 @@ ShaderState pgraph_get_shader_state(PGRAPHState *pg)
pgraph_reg_r(pg, NV_PGRAPH_SHADOWCTL), NV_PGRAPH_SHADOWCTL_SHADOW_ZFUNC);
state.fixed_function = fixed_function;
state.specular_enable = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_CSV0_C),
NV_PGRAPH_CSV0_C_SPECULAR_ENABLE);
/* fixed function stuff */
if (fixed_function) {

View File

@ -83,6 +83,7 @@ typedef struct ShaderState {
enum VshLight light[NV2A_MAX_LIGHTS];
bool fixed_function;
bool specular_enable;
/* vertex program */
bool vertex_program;

View File

@ -68,6 +68,8 @@
#ifndef HW_NVNET_REGS_H
#define HW_NVNET_REGS_H
// clang-format on
#define DEV_NEED_LASTPACKET1 0x0001
#define DEV_IRQMASK_1 0x0002
#define DEV_IRQMASK_2 0x0004
@ -291,4 +293,6 @@ enum {
#define LPA_LPACK 0x4000 /* Link partner acked us */
#define LPA_NPAGE 0x8000 /* Next page bit */
// clang-format off
#endif /* HW_NVNET_REGS_H */

View File

@ -3772,6 +3772,7 @@ if have_system
'hw/remote',
'hw/xbox/nv2a',
'hw/xbox/mcpx',
'hw/xbox/mcpx/dsp',
'hw/xbox',
]
endif

View File

@ -1,4 +1,4 @@
[wrap-git]
url=https://github.com/KhronosGroup/SPIRV-Reflect
revision=vulkan-sdk-1.4.304.0
revision=vulkan-sdk-1.4.309.0
depth=1

View File

@ -1,4 +1,4 @@
[wrap-git]
url=https://github.com/KhronosGroup/glslang
revision=vulkan-sdk-1.4.304.0
revision=vulkan-sdk-1.4.309.0
depth=1

View File

@ -86,3 +86,4 @@ subdir('qapi-schema')
subdir('qtest')
subdir('migration')
subdir('functional')
subdir('xbox')

View File

@ -0,0 +1,4 @@
all: basic
%: %.a56
a56 -o $@ $<

12
tests/xbox/dsp/data/basic Normal file
View File

@ -0,0 +1,12 @@
P 0000 0C0040
P 0040 0AF080
P 0041 000042
P 0042 56F400
P 0043 123456
P 0044 567000
P 0045 000003
P 0046 08F484
P 0047 000001
P 0048 0C0042
I 000040 start
I 000042 mainloop

View File

@ -0,0 +1,12 @@
org p:$0000
jmp <start
org p:$40
start
jmp mainloop
mainloop
move #$123456,A
move A,X:3
movep #$000001,x:$ffffc4
jmp <mainloop

View File

@ -0,0 +1,13 @@
test_env = environment()
test_env.set('G_TEST_SRCDIR', meson.current_source_dir())
test_env.set('G_TEST_BUILDDIR', meson.current_build_dir())
exe = executable('test-xbox-mcpx-dsp',
sources: files('test-dsp.c'),
dependencies: [qemuutil, dsp, glib])
test('xbox-mcpx-dsp', exe,
env: test_env,
args: ['--tap', '-k'],
protocol: 'tap',
suite: ['xbox', 'xbox-mcpx', 'xbox-mcpx-dsp'])

84
tests/xbox/dsp/test-dsp.c Normal file
View File

@ -0,0 +1,84 @@
/*
* DSP tests.
*
* Copyright (c) 2025 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/>.
*/
#include "qemu/osdep.h"
#include "hw/xbox/mcpx/dsp/dsp.h"
static void scratch_rw(void *opaque, uint8_t *ptr, uint32_t addr, size_t len, bool dir)
{
assert(!"Not implemented");
}
static void fifo_rw(void *opaque, uint8_t *ptr, unsigned int index, size_t len, bool dir)
{
assert(!"Not implemented");
}
static void load_prog(DSPState *s, const char *path)
{
FILE *file = fopen(path, "r");
assert(file && "Error opening file");
char type, line[100], arg1[20], arg2[20];
int addr, value;
while (fgets(line, sizeof(line), file)) {
if (sscanf(line, "%c %s %s", &type, arg1, arg2) >= 2) {
switch (type) {
case 'P':
case 'X':
case 'Y': {
assert(sscanf(arg1, "%x", &addr) == 1);
assert(sscanf(arg2, "%x", &value) == 1);
dsp_write_memory(s, type, addr, value);
break;
}
}
} else {
printf("Invalid line: %s\n", line);
assert(0);
}
}
fclose(file);
}
static void test_dsp_basic(void)
{
g_autofree gchar *path = g_test_build_filename(G_TEST_DIST, "data", "basic", NULL);
DSPState *s = dsp_init(NULL, scratch_rw, fifo_rw);
load_prog(s, path);
dsp_run(s, 1000);
uint32_t v = dsp_read_memory(s, 'X', 3);
g_assert_cmphex(v, ==, 0x123456);
dsp_destroy(s);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/basic", test_dsp_basic);
return g_test_run();
}

1
tests/xbox/meson.build Normal file
View File

@ -0,0 +1 @@
subdir('dsp')

View File

@ -6,10 +6,10 @@ FROM ubuntu:24.04
ENV MXE_PATH=/opt/mxe
ENV MXE_REPO=https://github.com/mxe/mxe.git
ENV MXE_VERSION=866492387740cc6580ef516c8a746402be645453
ENV MXE_VERSION=db7f5247eaab700f28bb9b9433d279e5958f5f01
ENV MXE_LLVM_MINGW_REPO=https://github.com/libvips/build-win64-mxe
ENV MXE_LLVM_MINGW_VERSION=32a2d0ecdfd3ea16bb4a99e333a8d5a3d63be491
ENV MXE_LLVM_MINGW_VERSION=51fc5884d0c4a3be43a2f09fa36a464885918d3d
ENV MXE_LLVM_MINGW_PATH=/opt/build-win64-mxe
ARG PLUGIN_DIRS="${MXE_LLVM_MINGW_PATH} ${MXE_LLVM_MINGW_PATH}/build/plugins/llvm-mingw"

View File

@ -2,10 +2,10 @@ PKG := vulkan-headers
$(PKG)_WEBSITE := https://github.com/KhronosGroup/Vulkan-Headers
$(PKG)_DESCR := Vulkan-Headers
$(PKG)_IGNORE :=
$(PKG)_VERSION := vulkan-sdk-1.4.304.0
$(PKG)_VERSION := vulkan-sdk-1.4.309.0
$(PKG)_SUBDIR := Vulkan-Headers-$($(PKG)_VERSION)
$(PKG)_FILE := vulkan-headers-$($(PKG)_VERSION).tar.gz
$(PKG)_CHECKSUM := 46f8f5b6384a36c688e0c40d28d534df41d22de406493dfb5c9b7bcc29672613
$(PKG)_CHECKSUM := 2bc1b4127950badc80212abf1edfa5c3b5032f3425edf37255863ba7592c1969
$(PKG)_URL := https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/$($(PKG)_VERSION).tar.gz
$(PKG)_DEPS := cc