mirror of https://github.com/xemu-project/xemu.git
Merge branch 'master' into enable_libusb
This commit is contained in:
commit
5bbf3027ac
|
@ -0,0 +1,8 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: "ci"
|
|
@ -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@1dc73863535b631f98b2378be8619f83b136f4a0 # v5
|
||||
with:
|
||||
context: ubuntu-win64-cross
|
||||
push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
|
|
|
@ -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@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 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@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 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@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 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@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 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.6.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@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 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@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 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@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 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@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
|
||||
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@d3f86a106a0bac45b974a628896c90dbdf5c8093 # 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
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
name: Bump Meson subprojects
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 6 * * 1'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
bump_wraps:
|
||||
name: "Bump Meson subprojects"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca # v6
|
||||
with:
|
||||
enable-cache: false
|
||||
|
||||
- name: Check for updates
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
uv run -s scripts/bump-subproject-wraps.py -m \
|
||||
subprojects/curl.wrap \
|
||||
subprojects/genconfig.wrap \
|
||||
subprojects/glslang.wrap \
|
||||
subprojects/imgui.wrap \
|
||||
subprojects/implot.wrap \
|
||||
subprojects/json.wrap \
|
||||
subprojects/nv2a_vsh_cpu.wrap \
|
||||
subprojects/SPIRV-Reflect.wrap \
|
||||
subprojects/tomlplusplus.wrap \
|
||||
subprojects/volk.wrap \
|
||||
subprojects/VulkanMemoryAllocator.wrap \
|
||||
subprojects/xxhash.wrap \
|
||||
> updated.json
|
||||
|
||||
- name: Create PRs for updates
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.XEMU_ROBOT_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git config user.name "xemu-robot"
|
||||
git config user.email "robot@xemu.app"
|
||||
|
||||
jq -c '.[]' updated.json | while read -r item; do
|
||||
path=$(echo "$item" | jq -r '.path')
|
||||
file_basename=$(basename "$path")
|
||||
name="${file_basename%%.*}"
|
||||
|
||||
owner=$(echo "$item" | jq -r '.owner')
|
||||
repo=$(echo "$item" | jq -r '.repo')
|
||||
old_rev=$(echo "$item" | jq -r '.old_rev')
|
||||
new_rev=$(echo "$item" | jq -r '.new_rev')
|
||||
new_tag=$(echo "$item" | jq -r '.new_tag')
|
||||
|
||||
echo "➤ Processing $name"
|
||||
branch="sync/bump-${name//\//-}-${GITHUB_RUN_ID}"
|
||||
|
||||
git switch --quiet -c "$branch" origin/master
|
||||
git add "$path"
|
||||
git commit -m "meson: Bump ${name} to ${new_tag}"
|
||||
git push -u origin "$branch"
|
||||
|
||||
gh pr create \
|
||||
--title "meson: Bump ${name} to ${new_tag}" \
|
||||
--body "Automatic bump of \`${name}\` to [${new_tag}](https://github.com/${owner}/${repo}/compare/${old_rev}..${new_rev})." \
|
||||
--base master
|
||||
done
|
|
@ -6,7 +6,6 @@
|
|||
.sdk
|
||||
.stgit-*
|
||||
.git-submodule-status
|
||||
.clang-format
|
||||
.gdb_history
|
||||
cscope.*
|
||||
tags
|
||||
|
|
10
build.sh
10
build.sh
|
@ -66,8 +66,14 @@ package_macos() {
|
|||
|
||||
cp Info.plist dist/xemu.app/Contents/
|
||||
|
||||
plutil -replace CFBundleShortVersionString -string $(cat ${project_source_dir}/XEMU_VERSION | cut -f1 -d-) dist/xemu.app/Contents/Info.plist
|
||||
plutil -replace CFBundleVersion -string $(cat ${project_source_dir}/XEMU_VERSION | cut -f1 -d-) dist/xemu.app/Contents/Info.plist
|
||||
if [[ -e "${project_source_dir}/XEMU_VERSION" ]]; then
|
||||
xemu_version="$(cat ${project_source_dir}/XEMU_VERSION | cut -f1 -d-)"
|
||||
else
|
||||
xemu_version="0.0.0"
|
||||
fi
|
||||
|
||||
plutil -replace CFBundleShortVersionString -string "${xemu_version}" dist/xemu.app/Contents/Info.plist
|
||||
plutil -replace CFBundleVersion -string "${xemu_version}" dist/xemu.app/Contents/Info.plist
|
||||
|
||||
codesign --force --deep --preserve-metadata=entitlements,requirements,flags,runtime --sign - "${exe_path}"
|
||||
python3 ./scripts/gen-license.py --version-file=macos-libs/$target_arch/INSTALLED > dist/LICENSE.txt
|
||||
|
|
|
@ -54,6 +54,9 @@ input:
|
|||
auto_bind:
|
||||
type: bool
|
||||
default: true
|
||||
allow_vibration:
|
||||
type: bool
|
||||
default: true
|
||||
background_input_capture: bool
|
||||
keyboard_controller_scancode_map:
|
||||
# Scancode reference : https://github.com/libsdl-org/SDL/blob/main/include/SDL_scancode.h
|
||||
|
|
|
@ -6,6 +6,8 @@ Build-Depends: debhelper (>= 11),
|
|||
cmake,
|
||||
git,
|
||||
python3:any,
|
||||
python3-pip,
|
||||
python3-tomli,
|
||||
python3-yaml,
|
||||
python3-venv,
|
||||
ninja-build,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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))
|
|
@ -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"
|
|
@ -0,0 +1 @@
|
|||
#include "trace/trace-hw_xbox_mcpx_dsp.h"
|
|
@ -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)
|
|
@ -155,8 +155,9 @@ static inline void nv2a_profile_inc_counter(enum NV2A_PROF_COUNTERS_ENUM cnt)
|
|||
void nv2a_dbg_renderdoc_init(void);
|
||||
void *nv2a_dbg_renderdoc_get_api(void);
|
||||
bool nv2a_dbg_renderdoc_available(void);
|
||||
void nv2a_dbg_renderdoc_capture_frames(int num_frames);
|
||||
void nv2a_dbg_renderdoc_capture_frames(int num_frames, bool trace);
|
||||
extern int renderdoc_capture_frames;
|
||||
extern bool renderdoc_trace_frames;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -423,7 +423,7 @@ const VMStateDescription vmstate_nv2a_pgraph_vertex_attributes = {
|
|||
|
||||
static const VMStateDescription vmstate_nv2a = {
|
||||
.name = "nv2a",
|
||||
.version_id = 2,
|
||||
.version_id = 3,
|
||||
.minimum_version_id = 1,
|
||||
.post_save = nv2a_post_save,
|
||||
.post_load = nv2a_post_load,
|
||||
|
@ -507,9 +507,11 @@ static const VMStateDescription vmstate_nv2a = {
|
|||
VMSTATE_BOOL_ARRAY(pgraph.ltc1_dirty, NV2AState, NV2A_LTC1_COUNT),
|
||||
VMSTATE_STRUCT_ARRAY(pgraph.vertex_attributes, NV2AState, NV2A_VERTEXSHADER_ATTRIBUTES, 1, vmstate_nv2a_pgraph_vertex_attributes, VertexAttribute),
|
||||
VMSTATE_UINT32(pgraph.inline_array_length, NV2AState),
|
||||
VMSTATE_UINT32_ARRAY(pgraph.inline_array, NV2AState, NV2A_MAX_BATCH_LENGTH),
|
||||
VMSTATE_UINT32_SUB_ARRAY(pgraph.inline_array, NV2AState, 0, NV2A_MAX_BATCH_LENGTH_V2),
|
||||
VMSTATE_UINT32_SUB_ARRAY_V(pgraph.inline_array, NV2AState, NV2A_MAX_BATCH_LENGTH_V2, NV2A_MAX_BATCH_LENGTH - NV2A_MAX_BATCH_LENGTH_V2, 3),
|
||||
VMSTATE_UINT32(pgraph.inline_elements_length, NV2AState), // fixme
|
||||
VMSTATE_UINT32_ARRAY(pgraph.inline_elements, NV2AState, NV2A_MAX_BATCH_LENGTH),
|
||||
VMSTATE_UINT32_SUB_ARRAY(pgraph.inline_elements, NV2AState, 0, NV2A_MAX_BATCH_LENGTH_V2),
|
||||
VMSTATE_UINT32_SUB_ARRAY_V(pgraph.inline_elements, NV2AState, NV2A_MAX_BATCH_LENGTH_V2, NV2A_MAX_BATCH_LENGTH - NV2A_MAX_BATCH_LENGTH_V2, 3),
|
||||
VMSTATE_UINT32(pgraph.inline_buffer_length, NV2AState), // fixme
|
||||
VMSTATE_UINT32(pgraph.draw_arrays_length, NV2AState),
|
||||
VMSTATE_UINT32(pgraph.draw_arrays_max_count, NV2AState),
|
||||
|
|
|
@ -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,11 +314,15 @@
|
|||
# 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_ALPHA_FROM_MATERIAL_SPECULAR (1 << 17)
|
||||
# define NV_PGRAPH_CSV0_C_SEPARATE_SPECULAR (1 << 18)
|
||||
# define NV_PGRAPH_CSV0_C_SPECULAR (3 << 19)
|
||||
# define NV_PGRAPH_CSV0_C_DIFFUSE (3 << 21)
|
||||
# define NV_PGRAPH_CSV0_C_AMBIENT (3 << 23)
|
||||
# define NV_PGRAPH_CSV0_C_EMISSION (3 << 25)
|
||||
# define NV_PGRAPH_CSV0_C_NORMALIZATION_ENABLE (1 << 27)
|
||||
# define NV_PGRAPH_CSV0_C_LOCALEYE (1 << 30)
|
||||
# define NV_PGRAPH_CSV0_C_LIGHTING (1 << 31)
|
||||
#define NV_PGRAPH_CSV1_B 0x00000FBC
|
||||
#define NV_PGRAPH_CSV1_A 0x00000FC0
|
||||
|
@ -879,6 +885,10 @@
|
|||
# define NV097_SET_CONTROL0_STENCIL_WRITE_ENABLE (1 << 0)
|
||||
# define NV097_SET_CONTROL0_Z_FORMAT (1 << 12)
|
||||
# define NV097_SET_CONTROL0_Z_PERSPECTIVE_ENABLE (1 << 16)
|
||||
# define NV097_SET_LIGHT_CONTROL 0x00000294
|
||||
# define NV097_SET_LIGHT_CONTROL_SEPARATE_SPECULAR 1
|
||||
# define NV097_SET_LIGHT_CONTROL_LOCALEYE (1 << 16)
|
||||
# define NV097_SET_LIGHT_CONTROL_ALPHA_FROM_MATERIAL_SPECULAR (1 << 17)
|
||||
# define NV097_SET_COLOR_MATERIAL 0x00000298
|
||||
# define NV097_SET_FOG_MODE 0x0000029C
|
||||
# define NV097_SET_FOG_MODE_V_LINEAR 0x2601
|
||||
|
@ -1013,6 +1023,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
|
||||
|
@ -1045,6 +1056,7 @@
|
|||
# define NV097_SET_TEXGEN_VIEW_MODEL_LOCAL_VIEWER 0
|
||||
# define NV097_SET_TEXGEN_VIEW_MODEL_INFINITE_VIEWER 1
|
||||
# define NV097_SET_FOG_PLANE 0x000009D0
|
||||
# define NV097_SET_SPECULAR_PARAMS 0x000009E0
|
||||
# define NV097_SET_SCENE_AMBIENT_COLOR 0x00000A10
|
||||
# define NV097_SET_VIEWPORT_OFFSET 0x00000A20
|
||||
# define NV097_SET_POINT_PARAMS 0x00000A30
|
||||
|
@ -1095,6 +1107,11 @@
|
|||
# define NV097_SET_TEXCOORD3_4F 0x00001620
|
||||
# define NV097_SET_TEXCOORD3_2S 0x00001610
|
||||
# define NV097_SET_TEXCOORD3_4S 0x00001630
|
||||
# define NV097_SET_FOG_COORD 0x00001698
|
||||
# define NV097_SET_WEIGHT1F 0x0000169C
|
||||
# define NV097_SET_WEIGHT2F 0x000016A0
|
||||
# define NV097_SET_WEIGHT3F 0x000016B0
|
||||
# define NV097_SET_WEIGHT4F 0x000016C0
|
||||
# define NV097_SET_VERTEX_DATA_ARRAY_OFFSET 0x00001720
|
||||
# define NV097_SET_VERTEX_DATA_ARRAY_FORMAT 0x00001760
|
||||
# define NV097_SET_VERTEX_DATA_ARRAY_FORMAT_TYPE 0x0000000F
|
||||
|
@ -1248,6 +1265,7 @@
|
|||
# define NV097_SET_CLEAR_RECT_HORIZONTAL 0x00001D98
|
||||
# define NV097_SET_CLEAR_RECT_VERTICAL 0x00001D9C
|
||||
# define NV097_SET_SPECULAR_FOG_FACTOR 0x00001E20
|
||||
# define NV097_SET_SPECULAR_PARAMS_BACK 0x00001E28
|
||||
# define NV097_SET_COMBINER_COLOR_OCW 0x00001E40
|
||||
# define NV097_SET_COMBINER_CONTROL 0x00001E60
|
||||
# define NV097_SET_SHADOW_ZSLOPE_THRESHOLD 0x00001E68
|
||||
|
@ -1449,7 +1467,22 @@
|
|||
#define NV2A_NUM_SUBCHANNELS 8
|
||||
#define NV2A_CACHE1_SIZE 128
|
||||
|
||||
#define NV2A_MAX_BATCH_LENGTH 0x1FFFF
|
||||
/* This is a multi-use limit. Testing on an Xbox 1.0, it is possible to send
|
||||
* arrays of at least 0x0FFFFF elements without issue, however sending
|
||||
* NV097_DRAW_ARRAYS with a start value > 0xFFFF raises an exception implying
|
||||
* that there may be a vertex limit. Since xemu uses batch length for vertex
|
||||
* elements in NV097_INLINE_ARRAY the size should ideally be high enough to
|
||||
* accommodate 0xFFFF vertices with maximum attributes specified.
|
||||
*
|
||||
* Retail games are known to send at least 0x410FA elements in a single draw, so
|
||||
* a somewhat larger value is selected to balance memory use with real-world
|
||||
* limits.
|
||||
*
|
||||
* NV2A_MAX_BATCH_LENGTH_V2 is the previous limit, for migration.
|
||||
* FIXME: Remove NV2A_MAX_BATCH_LENGTH_V2 at some point in the future.
|
||||
*/
|
||||
#define NV2A_MAX_BATCH_LENGTH 0x07FFFF
|
||||
#define NV2A_MAX_BATCH_LENGTH_V2 0x1FFFF
|
||||
#define NV2A_VERTEXSHADER_ATTRIBUTES 16
|
||||
#define NV2A_MAX_TEXTURES 4
|
||||
|
||||
|
@ -1463,4 +1496,6 @@
|
|||
|
||||
#define NV2A_CUBEMAP_FACE_ALIGNMENT 128
|
||||
|
||||
// clang-format on
|
||||
|
||||
#endif
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
static RENDERDOC_API_1_6_0 *rdoc_api = NULL;
|
||||
|
||||
int renderdoc_capture_frames = 0;
|
||||
bool renderdoc_trace_frames = false;
|
||||
|
||||
void nv2a_dbg_renderdoc_init(void)
|
||||
{
|
||||
|
@ -89,7 +90,8 @@ bool nv2a_dbg_renderdoc_available(void)
|
|||
return rdoc_api != NULL;
|
||||
}
|
||||
|
||||
void nv2a_dbg_renderdoc_capture_frames(int num_frames)
|
||||
void nv2a_dbg_renderdoc_capture_frames(int num_frames, bool trace)
|
||||
{
|
||||
renderdoc_capture_frames += num_frames;
|
||||
renderdoc_trace_frames = trace;
|
||||
}
|
||||
|
|
|
@ -298,7 +298,7 @@ static const SurfaceFormatInfo kelvin_surface_color_format_gl_map[] = {
|
|||
[NV097_SET_SURFACE_FORMAT_COLOR_LE_B8] =
|
||||
{1, GL_R8, GL_RED, GL_UNSIGNED_BYTE, GL_COLOR_ATTACHMENT0},
|
||||
[NV097_SET_SURFACE_FORMAT_COLOR_LE_G8B8] =
|
||||
{2, GL_RG8, GL_RG, GL_UNSIGNED_SHORT, GL_COLOR_ATTACHMENT0},
|
||||
{2, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, GL_COLOR_ATTACHMENT0},
|
||||
};
|
||||
|
||||
static const SurfaceFormatInfo kelvin_surface_zeta_float_format_gl_map[] = {
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <assert.h>
|
||||
|
||||
#ifdef CONFIG_RENDERDOC
|
||||
#include "trace/control.h"
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||
#include "thirdparty/renderdoc_app.h"
|
||||
#endif
|
||||
|
@ -154,7 +156,8 @@ void gl_debug_frame_terminator(void)
|
|||
RENDERDOC_API_1_6_0 *rdoc_api = nv2a_dbg_renderdoc_get_api();
|
||||
|
||||
if (rdoc_api->IsTargetControlConnected()) {
|
||||
if (rdoc_api->IsFrameCapturing()) {
|
||||
bool capturing = rdoc_api->IsFrameCapturing();
|
||||
if (capturing && renderdoc_capture_frames == 0) {
|
||||
rdoc_api->EndFrameCapture(NULL, NULL);
|
||||
GLenum error = glGetError();
|
||||
if (error != GL_NO_ERROR) {
|
||||
|
@ -162,14 +165,23 @@ void gl_debug_frame_terminator(void)
|
|||
"Renderdoc EndFrameCapture triggered GL error 0x%X - ignoring\n",
|
||||
error);
|
||||
}
|
||||
if (renderdoc_trace_frames) {
|
||||
trace_enable_events("-nv2a_pgraph_*");
|
||||
renderdoc_trace_frames = false;
|
||||
}
|
||||
}
|
||||
if (renderdoc_capture_frames > 0) {
|
||||
rdoc_api->StartFrameCapture(NULL, NULL);
|
||||
GLenum error = glGetError();
|
||||
if (error != GL_NO_ERROR) {
|
||||
fprintf(stderr,
|
||||
"Renderdoc StartFrameCapture triggered GL error 0x%X - ignoring\n",
|
||||
error);
|
||||
if (!capturing) {
|
||||
if (renderdoc_trace_frames) {
|
||||
trace_enable_events("nv2a_pgraph_*");
|
||||
}
|
||||
rdoc_api->StartFrameCapture(NULL, NULL);
|
||||
GLenum error = glGetError();
|
||||
if (error != GL_NO_ERROR) {
|
||||
fprintf(stderr,
|
||||
"Renderdoc StartFrameCapture triggered GL error 0x%X - ignoring\n",
|
||||
error);
|
||||
}
|
||||
}
|
||||
--renderdoc_capture_frames;
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ void pgraph_gl_init_display(NV2AState *d)
|
|||
"{\n"
|
||||
" vec2 texCoord = gl_FragCoord.xy/display_size;\n"
|
||||
" float rel = display_size.y/textureSize(tex, 0).y/line_offset;\n"
|
||||
" texCoord.y = 1 + rel*(texCoord.y - 1);"
|
||||
" texCoord.y = rel*(1.0f - texCoord.y);"
|
||||
" out_Color.rgba = texture(tex, texCoord);\n"
|
||||
" if (pvideo_enable) {\n"
|
||||
" vec2 screenCoord = gl_FragCoord.xy - 0.5;\n"
|
||||
|
|
|
@ -92,7 +92,6 @@ void pgraph_gl_clear_surface(NV2AState *d, uint32_t parameter)
|
|||
scissor_height = ymax - ymin + 1;
|
||||
pgraph_apply_anti_aliasing_factor(pg, &xmin, &ymin);
|
||||
pgraph_apply_anti_aliasing_factor(pg, &scissor_width, &scissor_height);
|
||||
ymin = pg->surface_binding_dim.height - (ymin + scissor_height);
|
||||
|
||||
NV2A_DPRINTF("Translated clear rect to %d,%d - %d,%d\n", xmin, ymin,
|
||||
xmin + scissor_width - 1, ymin + scissor_height - 1);
|
||||
|
@ -204,9 +203,10 @@ void pgraph_gl_draw_begin(NV2AState *d)
|
|||
}
|
||||
|
||||
/* Front-face select */
|
||||
/* Winding is reverse here because clip-space y-coordinates are inverted */
|
||||
glFrontFace(pgraph_reg_r(pg, NV_PGRAPH_SETUPRASTER)
|
||||
& NV_PGRAPH_SETUPRASTER_FRONTFACE
|
||||
? GL_CCW : GL_CW);
|
||||
? GL_CW : GL_CCW);
|
||||
|
||||
/* Polygon offset */
|
||||
/* FIXME: GL implementation-specific, maybe do this in VS? */
|
||||
|
@ -340,7 +340,6 @@ void pgraph_gl_draw_begin(NV2AState *d)
|
|||
|
||||
pgraph_apply_anti_aliasing_factor(pg, &xmin, &ymin);
|
||||
pgraph_apply_anti_aliasing_factor(pg, &scissor_width, &scissor_height);
|
||||
ymin = pg->surface_binding_dim.height - (ymin + scissor_height);
|
||||
pgraph_apply_scaling_factor(pg, &xmin, &ymin);
|
||||
pgraph_apply_scaling_factor(pg, &scissor_width, &scissor_height);
|
||||
|
||||
|
|
|
@ -111,7 +111,6 @@ typedef struct ShaderBinding {
|
|||
GLint vsh_constant_loc[NV2A_VERTEXSHADER_CONSTANTS];
|
||||
uint32_t vsh_constants[NV2A_VERTEXSHADER_CONSTANTS][4];
|
||||
|
||||
GLint inv_viewport_loc;
|
||||
GLint ltctxa_loc[NV2A_LTCTXA_COUNT];
|
||||
GLint ltctxb_loc[NV2A_LTCTXB_COUNT];
|
||||
GLint ltc1_loc[NV2A_LTC1_COUNT];
|
||||
|
@ -122,9 +121,11 @@ typedef struct ShaderBinding {
|
|||
GLint light_infinite_direction_loc[NV2A_MAX_LIGHTS];
|
||||
GLint light_local_position_loc[NV2A_MAX_LIGHTS];
|
||||
GLint light_local_attenuation_loc[NV2A_MAX_LIGHTS];
|
||||
int specular_power_loc;
|
||||
|
||||
GLint clip_region_loc[8];
|
||||
|
||||
GLint point_params_loc[8];
|
||||
GLint material_alpha_loc;
|
||||
} ShaderBinding;
|
||||
|
||||
|
|
|
@ -158,7 +158,6 @@ static void update_shader_constant_locations(ShaderBinding *binding)
|
|||
binding->fog_color_loc = glGetUniformLocation(binding->gl_program, "fogColor");
|
||||
binding->fog_param_loc = glGetUniformLocation(binding->gl_program, "fogParam");
|
||||
|
||||
binding->inv_viewport_loc = glGetUniformLocation(binding->gl_program, "invViewport");
|
||||
for (int i = 0; i < NV2A_LTCTXA_COUNT; i++) {
|
||||
snprintf(tmp, sizeof(tmp), "ltctxa[%d]", i);
|
||||
binding->ltctxa_loc[i] = glGetUniformLocation(binding->gl_program, tmp);
|
||||
|
@ -190,11 +189,19 @@ static void update_shader_constant_locations(ShaderBinding *binding)
|
|||
binding->clip_region_loc[i] = glGetUniformLocation(binding->gl_program, tmp);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
snprintf(tmp, sizeof(tmp), "pointParams[%d]", i);
|
||||
binding->point_params_loc[i] = glGetUniformLocation(binding->gl_program, tmp);
|
||||
}
|
||||
|
||||
if (binding->state.fixed_function) {
|
||||
binding->material_alpha_loc =
|
||||
glGetUniformLocation(binding->gl_program, "material_alpha");
|
||||
binding->specular_power_loc =
|
||||
glGetUniformLocation(binding->gl_program, "specularPower");
|
||||
} else {
|
||||
binding->material_alpha_loc = -1;
|
||||
binding->specular_power_loc = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -836,27 +843,9 @@ static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding,
|
|||
}
|
||||
}
|
||||
|
||||
/* estimate the viewport by assuming it matches the surface ... */
|
||||
unsigned int aa_width = 1, aa_height = 1;
|
||||
pgraph_apply_anti_aliasing_factor(pg, &aa_width, &aa_height);
|
||||
|
||||
float m11 = 0.5 * (pg->surface_binding_dim.width/aa_width);
|
||||
float m22 = -0.5 * (pg->surface_binding_dim.height/aa_height);
|
||||
float m33 = zmax;
|
||||
float m41 = *(float*)&pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPOFF][0];
|
||||
float m42 = *(float*)&pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPOFF][1];
|
||||
|
||||
float invViewport[16] = {
|
||||
1.0/m11, 0, 0, 0,
|
||||
0, 1.0/m22, 0, 0,
|
||||
0, 0, 1.0/m33, 0,
|
||||
-1.0+m41/m11, 1.0+m42/m22, 0, 1.0
|
||||
};
|
||||
|
||||
if (binding->inv_viewport_loc != -1) {
|
||||
glUniformMatrix4fv(binding->inv_viewport_loc,
|
||||
1, GL_FALSE, &invViewport[0]);
|
||||
}
|
||||
if (binding->specular_power_loc != -1) {
|
||||
glUniform1f(binding->specular_power_loc, pg->specular_power);
|
||||
}
|
||||
}
|
||||
|
||||
/* update vertex program constants */
|
||||
|
@ -935,12 +924,15 @@ static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding,
|
|||
pgraph_apply_scaling_factor(pg, &x_min, &y_min);
|
||||
pgraph_apply_scaling_factor(pg, &x_max, &y_max);
|
||||
|
||||
/* Translate for the GL viewport origin */
|
||||
int y_min_xlat = MAX((int)max_gl_height - (int)y_max, 0);
|
||||
int y_max_xlat = MIN((int)max_gl_height - (int)y_min, max_gl_height);
|
||||
|
||||
glUniform4i(r->shader_binding->clip_region_loc[i],
|
||||
x_min, y_min_xlat, x_max, y_max_xlat);
|
||||
x_min, y_min, x_max, y_max);
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; ++i) {
|
||||
GLint loc = binding->point_params_loc[i];
|
||||
if (loc != -1) {
|
||||
glUniform1f(loc, pg->point_params[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (binding->material_alpha_loc != -1) {
|
||||
|
|
|
@ -137,11 +137,7 @@ static void init_render_to_texture(PGRAPHState *pg)
|
|||
"layout(location = 0) out vec4 out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" vec2 texCoord;\n"
|
||||
" texCoord.x = gl_FragCoord.x;\n"
|
||||
" texCoord.y = (surface_size.y - gl_FragCoord.y)\n"
|
||||
" + (textureSize(tex,0).y - surface_size.y);\n"
|
||||
" texCoord /= textureSize(tex,0).xy;\n"
|
||||
" vec2 texCoord = gl_FragCoord.xy / textureSize(tex, 0).xy;\n"
|
||||
" out_Color.rgba = texture(tex, texCoord);\n"
|
||||
"}\n";
|
||||
|
||||
|
@ -298,7 +294,7 @@ static void render_surface_to_texture_slow(NV2AState *d,
|
|||
size_t bufsize = width * height * surface->fmt.bytes_per_pixel;
|
||||
|
||||
uint8_t *buf = g_malloc(bufsize);
|
||||
surface_download_to_buffer(d, surface, false, true, false, buf);
|
||||
surface_download_to_buffer(d, surface, false, false, false, buf);
|
||||
|
||||
width = texture_shape->width;
|
||||
height = texture_shape->height;
|
||||
|
@ -738,7 +734,7 @@ static void surface_download(NV2AState *d, SurfaceBinding *surface, bool force)
|
|||
|
||||
nv2a_profile_inc_counter(NV2A_PROF_SURF_DOWNLOAD);
|
||||
|
||||
surface_download_to_buffer(d, surface, true, true, true,
|
||||
surface_download_to_buffer(d, surface, true, false, true,
|
||||
d->vram_ptr + surface->vram_addr);
|
||||
|
||||
memory_region_set_client_dirty(d->vram, surface->vram_addr,
|
||||
|
@ -875,20 +871,26 @@ void pgraph_gl_upload_surface_data(NV2AState *d, SurfaceBinding *surface,
|
|||
surface->fmt.bytes_per_pixel);
|
||||
}
|
||||
|
||||
/* FIXME: Replace this flip/scaling */
|
||||
/* FIXME: Replace this scaling */
|
||||
|
||||
// This is VRAM so we can't do this inplace!
|
||||
uint8_t *flipped_buf = (uint8_t *)g_malloc(
|
||||
surface->height * surface->width * surface->fmt.bytes_per_pixel);
|
||||
unsigned int irow;
|
||||
for (irow = 0; irow < surface->height; irow++) {
|
||||
memcpy(&flipped_buf[surface->width * (surface->height - irow - 1)
|
||||
* surface->fmt.bytes_per_pixel],
|
||||
&buf[surface->pitch * irow],
|
||||
surface->width * surface->fmt.bytes_per_pixel);
|
||||
uint8_t *optimal_buf = buf;
|
||||
unsigned int optimal_pitch = surface->width * surface->fmt.bytes_per_pixel;
|
||||
|
||||
if (surface->pitch != optimal_pitch) {
|
||||
optimal_buf = (uint8_t *)g_malloc(surface->height * optimal_pitch);
|
||||
|
||||
uint8_t *src = buf;
|
||||
uint8_t *dst = optimal_buf;
|
||||
unsigned int irow;
|
||||
for (irow = 0; irow < surface->height; irow++) {
|
||||
memcpy(dst, src, optimal_pitch);
|
||||
src += surface->pitch;
|
||||
dst += optimal_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *gl_read_buf = flipped_buf;
|
||||
uint8_t *gl_read_buf = optimal_buf;
|
||||
unsigned int width = surface->width, height = surface->height;
|
||||
|
||||
if (pg->surface_scale_factor > 1) {
|
||||
|
@ -896,7 +898,7 @@ void pgraph_gl_upload_surface_data(NV2AState *d, SurfaceBinding *surface,
|
|||
pg->scale_buf = (uint8_t *)g_realloc(
|
||||
pg->scale_buf, width * height * surface->fmt.bytes_per_pixel);
|
||||
gl_read_buf = pg->scale_buf;
|
||||
uint8_t *out = gl_read_buf, *in = flipped_buf;
|
||||
uint8_t *out = gl_read_buf, *in = optimal_buf;
|
||||
surface_copy_expand(out, in, surface->width, surface->height,
|
||||
surface->fmt.bytes_per_pixel,
|
||||
d->pgraph.surface_scale_factor);
|
||||
|
@ -915,7 +917,9 @@ void pgraph_gl_upload_surface_data(NV2AState *d, SurfaceBinding *surface,
|
|||
height, 0, surface->fmt.gl_format, surface->fmt.gl_type,
|
||||
gl_read_buf);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, prev_unpack_alignment);
|
||||
g_free(flipped_buf);
|
||||
if (optimal_buf != buf) {
|
||||
g_free(optimal_buf);
|
||||
}
|
||||
if (surface->swizzle) {
|
||||
g_free(buf);
|
||||
}
|
||||
|
|
|
@ -949,7 +949,7 @@ static MString* psh_convert(struct PixelShader *ps)
|
|||
|
||||
switch (ps->tex_modes[i]) {
|
||||
case PS_TEXTUREMODES_NONE:
|
||||
mstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_NONE */\n",
|
||||
mstring_append_fmt(vars, "vec4 t%d = vec4(0.0, 0.0, 0.0, 1.0); /* PS_TEXTUREMODES_NONE */\n",
|
||||
i);
|
||||
break;
|
||||
case PS_TEXTUREMODES_PROJECT2D: {
|
||||
|
@ -1123,8 +1123,8 @@ static MString* psh_convert(struct PixelShader *ps)
|
|||
i, i-2, i-1, i);
|
||||
|
||||
apply_border_adjustment(ps, vars, i, "dotSTR%d");
|
||||
mstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, dotSTR%d);\n",
|
||||
i, i, i);
|
||||
mstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, %s(dotSTR%d%s));\n",
|
||||
i, i, tex_remap, i, ps->state.dim_tex[i] == 2 ? ".xy" : "");
|
||||
break;
|
||||
case PS_TEXTUREMODES_DOT_STR_CUBE:
|
||||
assert(i == 3);
|
||||
|
|
|
@ -115,8 +115,6 @@ GLSL_DEFINE(sceneAmbientColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_FR_AMB) ".xyz")
|
|||
GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz")
|
||||
"\n"
|
||||
);
|
||||
mstring_append_fmt(uniforms,
|
||||
"%smat4 invViewport;\n", u);
|
||||
|
||||
/* Skinning */
|
||||
unsigned int count;
|
||||
|
@ -230,10 +228,16 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
|
|||
}
|
||||
|
||||
/* Lighting */
|
||||
if (state->lighting) {
|
||||
|
||||
if (!state->lighting) {
|
||||
mstring_append(body, " oD0 = diffuse;\n");
|
||||
mstring_append(body, " oD1 = specular;\n");
|
||||
mstring_append(body, " oB0 = backDiffuse;\n");
|
||||
mstring_append(body, " oB1 = backSpecular;\n");
|
||||
} else {
|
||||
//FIXME: Do 2 passes if we want 2 sided-lighting?
|
||||
|
||||
mstring_append_fmt(uniforms, "%sfloat specularPower;\n", u);
|
||||
|
||||
static char alpha_source_diffuse[] = "diffuse.a";
|
||||
static char alpha_source_specular[] = "specular.a";
|
||||
static char alpha_source_material[] = "material_alpha";
|
||||
|
@ -264,17 +268,17 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
|
|||
|
||||
mstring_append(body, "oD1 = vec4(0.0, 0.0, 0.0, specular.a);\n");
|
||||
|
||||
if (state->local_eye) {
|
||||
mstring_append(body,
|
||||
"vec3 VPeye = normalize(eyePosition.xyz / eyePosition.w - tPosition.xyz / tPosition.w);\n"
|
||||
);
|
||||
}
|
||||
|
||||
for (i = 0; i < NV2A_MAX_LIGHTS; i++) {
|
||||
if (state->light[i] == LIGHT_OFF) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* FIXME: It seems that we only have to handle the surface colors if
|
||||
* they are not part of the material [= vertex colors].
|
||||
* If they are material the cpu will premultiply light
|
||||
* colors
|
||||
*/
|
||||
|
||||
mstring_append_fmt(body, "/* Light %d */ {\n", i);
|
||||
|
||||
if (state->light[i] == LIGHT_LOCAL
|
||||
|
@ -285,18 +289,20 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
|
|||
"%svec3 lightLocalAttenuation%d;\n",
|
||||
u, i, u, i);
|
||||
mstring_append_fmt(body,
|
||||
" vec3 VP = lightLocalPosition%d - tPosition.xyz/tPosition.w;\n"
|
||||
" vec3 tPos = tPosition.xyz/tPosition.w;\n"
|
||||
" vec3 VP = lightLocalPosition%d - tPos;\n"
|
||||
" float d = length(VP);\n"
|
||||
//FIXME: if (d > lightLocalRange) { .. don't process this light .. } /* inclusive?! */ - what about directional lights?
|
||||
" VP = normalize(VP);\n"
|
||||
" float attenuation = 1.0 / (lightLocalAttenuation%d.x\n"
|
||||
" + lightLocalAttenuation%d.y * d\n"
|
||||
" + lightLocalAttenuation%d.z * d * d);\n"
|
||||
" vec3 halfVector = normalize(VP + eyePosition.xyz / eyePosition.w);\n" /* FIXME: Not sure if eyePosition is correct */
|
||||
" float nDotVP = max(0.0, dot(tNormal, VP));\n"
|
||||
" float nDotHV = max(0.0, dot(tNormal, halfVector));\n",
|
||||
i, i, i, i);
|
||||
|
||||
" if (d <= lightLocalRange(%d)) {\n" /* FIXME: Double check that range is inclusive */
|
||||
" VP = normalize(VP);\n"
|
||||
" float attenuation = 1.0 / (lightLocalAttenuation%d.x\n"
|
||||
" + lightLocalAttenuation%d.y * d\n"
|
||||
" + lightLocalAttenuation%d.z * d * d);\n"
|
||||
" vec3 halfVector = normalize(VP + %s);\n"
|
||||
" float nDotVP = max(0.0, dot(tNormal, VP));\n"
|
||||
" float nDotHV = max(0.0, dot(tNormal, halfVector));\n",
|
||||
i, i, i, i, i,
|
||||
state->local_eye ? "VPeye" : "vec3(0.0, 0.0, 0.0)"
|
||||
);
|
||||
}
|
||||
|
||||
switch(state->light[i]) {
|
||||
|
@ -309,15 +315,21 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
|
|||
"%svec3 lightInfiniteDirection%d;\n",
|
||||
u, i, u, i);
|
||||
mstring_append_fmt(body,
|
||||
" float attenuation = 1.0;\n"
|
||||
" float nDotVP = max(0.0, dot(tNormal, normalize(vec3(lightInfiniteDirection%d))));\n"
|
||||
" float nDotHV = max(0.0, dot(tNormal, vec3(lightInfiniteHalfVector%d)));\n",
|
||||
i, i);
|
||||
|
||||
/* FIXME: Do specular */
|
||||
|
||||
/* FIXME: tBackDiffuse */
|
||||
|
||||
" {\n"
|
||||
" float attenuation = 1.0;\n"
|
||||
" vec3 lightDirection = normalize(lightInfiniteDirection%d);\n"
|
||||
" float nDotVP = max(0.0, dot(tNormal, lightDirection));\n",
|
||||
i);
|
||||
if (state->local_eye) {
|
||||
mstring_append(body,
|
||||
" float nDotHV = max(0.0, dot(tNormal, normalize(lightDirection + VPeye)));\n"
|
||||
);
|
||||
} else {
|
||||
mstring_append_fmt(body,
|
||||
" float nDotHV = max(0.0, dot(tNormal, lightInfiniteHalfVector%d));\n",
|
||||
i
|
||||
);
|
||||
}
|
||||
break;
|
||||
case LIGHT_LOCAL:
|
||||
/* Everything done already */
|
||||
|
@ -325,18 +337,18 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
|
|||
case LIGHT_SPOT:
|
||||
/* https://docs.microsoft.com/en-us/windows/win32/direct3d9/attenuation-and-spotlight-factor#spotlight-factor */
|
||||
mstring_append_fmt(body,
|
||||
" vec4 spotDir = lightSpotDirection(%d);\n"
|
||||
" float invScale = 1/length(spotDir.xyz);\n"
|
||||
" float cosHalfPhi = -invScale*spotDir.w;\n"
|
||||
" float cosHalfTheta = invScale + cosHalfPhi;\n"
|
||||
" float spotDirDotVP = dot(spotDir.xyz, VP);\n"
|
||||
" float rho = invScale*spotDirDotVP;\n"
|
||||
" if (rho > cosHalfTheta) {\n"
|
||||
" } else if (rho <= cosHalfPhi) {\n"
|
||||
" attenuation = 0.0;\n"
|
||||
" } else {\n"
|
||||
" attenuation *= spotDirDotVP + spotDir.w;\n" /* FIXME: lightSpotFalloff */
|
||||
" }\n",
|
||||
" vec4 spotDir = lightSpotDirection(%d);\n"
|
||||
" float invScale = 1/length(spotDir.xyz);\n"
|
||||
" float cosHalfPhi = -invScale*spotDir.w;\n"
|
||||
" float cosHalfTheta = invScale + cosHalfPhi;\n"
|
||||
" float spotDirDotVP = dot(spotDir.xyz, VP);\n"
|
||||
" float rho = invScale*spotDirDotVP;\n"
|
||||
" if (rho > cosHalfTheta) {\n"
|
||||
" } else if (rho <= cosHalfPhi) {\n"
|
||||
" attenuation = 0.0;\n"
|
||||
" } else {\n"
|
||||
" attenuation *= spotDirDotVP + spotDir.w;\n" /* FIXME: lightSpotFalloff */
|
||||
" }\n",
|
||||
i);
|
||||
break;
|
||||
default:
|
||||
|
@ -345,46 +357,82 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
|
|||
}
|
||||
|
||||
mstring_append_fmt(body,
|
||||
" float pf;\n"
|
||||
" if (nDotVP == 0.0) {\n"
|
||||
" pf = 0.0;\n"
|
||||
" } else {\n"
|
||||
" pf = pow(nDotHV, /* specular(l, m, n, l1, m1, n1) */ 0.001);\n"
|
||||
" }\n"
|
||||
" vec3 lightAmbient = lightAmbientColor(%d) * attenuation;\n"
|
||||
" vec3 lightDiffuse = lightDiffuseColor(%d) * attenuation * nDotVP;\n"
|
||||
" vec3 lightSpecular = lightSpecularColor(%d) * pf;\n",
|
||||
" float pf;\n"
|
||||
" if (nDotVP == 0.0 || nDotHV == 0.0) {\n"
|
||||
" pf = 0.0;\n"
|
||||
" } else {\n"
|
||||
" pf = pow(nDotHV, specularPower);\n"
|
||||
" }\n"
|
||||
" vec3 lightAmbient = lightAmbientColor(%d) * attenuation;\n"
|
||||
" vec3 lightDiffuse = lightDiffuseColor(%d) * attenuation * nDotVP;\n"
|
||||
" vec3 lightSpecular = lightSpecularColor(%d) * attenuation * pf;\n",
|
||||
i, i, i);
|
||||
|
||||
mstring_append(body,
|
||||
" oD0.xyz += lightAmbient;\n");
|
||||
" oD0.xyz += lightAmbient;\n");
|
||||
|
||||
switch (state->diffuse_src) {
|
||||
case MATERIAL_COLOR_SRC_MATERIAL:
|
||||
mstring_append(body,
|
||||
" oD0.xyz += lightDiffuse;\n");
|
||||
" oD0.xyz += lightDiffuse;\n");
|
||||
break;
|
||||
case MATERIAL_COLOR_SRC_DIFFUSE:
|
||||
mstring_append(body,
|
||||
" oD0.xyz += diffuse.xyz * lightDiffuse;\n");
|
||||
" oD0.xyz += diffuse.xyz * lightDiffuse;\n");
|
||||
break;
|
||||
case MATERIAL_COLOR_SRC_SPECULAR:
|
||||
mstring_append(body,
|
||||
" oD0.xyz += specular.xyz * lightDiffuse;\n");
|
||||
" oD0.xyz += specular.xyz * lightDiffuse;\n");
|
||||
break;
|
||||
}
|
||||
|
||||
mstring_append(body,
|
||||
" oD1.xyz += specular.xyz * lightSpecular;\n");
|
||||
switch (state->specular_src) {
|
||||
case MATERIAL_COLOR_SRC_MATERIAL:
|
||||
mstring_append(body,
|
||||
" oD1.xyz += lightSpecular;\n");
|
||||
break;
|
||||
case MATERIAL_COLOR_SRC_DIFFUSE:
|
||||
mstring_append(body,
|
||||
" oD1.xyz += diffuse.xyz * lightSpecular;\n");
|
||||
break;
|
||||
case MATERIAL_COLOR_SRC_SPECULAR:
|
||||
mstring_append(body,
|
||||
" oD1.xyz += specular.xyz * lightSpecular;\n");
|
||||
break;
|
||||
}
|
||||
|
||||
mstring_append(body, "}\n");
|
||||
mstring_append(body, " }\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
/* TODO: Implement two-sided lighting */
|
||||
mstring_append(body, " oB0 = backDiffuse;\n");
|
||||
mstring_append(body, " oB1 = backSpecular;\n");
|
||||
}
|
||||
|
||||
if (!state->specular_enable) {
|
||||
mstring_append(body, " oD1 = vec4(0.0, 0.0, 0.0, 1.0);\n");
|
||||
mstring_append(body, " oB1 = vec4(0.0, 0.0, 0.0, 1.0);\n");
|
||||
} else {
|
||||
if (!state->separate_specular) {
|
||||
if (state->lighting) {
|
||||
mstring_append(body,
|
||||
" oD0.xyz += oD1.xyz;\n"
|
||||
" oB0.xyz += oB1.xyz;\n"
|
||||
);
|
||||
}
|
||||
mstring_append(body,
|
||||
" oD1 = specular;\n"
|
||||
" oB1 = backSpecular;\n"
|
||||
);
|
||||
}
|
||||
if (state->ignore_specular_alpha) {
|
||||
mstring_append(body,
|
||||
" oD1.a = 1.0;\n"
|
||||
" oB1.a = 1.0;\n"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
mstring_append(body, " oD0 = diffuse;\n");
|
||||
mstring_append(body, " oD1 = specular;\n");
|
||||
}
|
||||
mstring_append(body, " oB0 = backDiffuse;\n");
|
||||
mstring_append(body, " oB1 = backSpecular;\n");
|
||||
|
||||
/* Fog */
|
||||
if (state->fog_enable) {
|
||||
|
@ -409,7 +457,7 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
|
|||
mstring_append(body, " float fogDistance = fogCoord;\n");
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
assert(!"Invalid foggen mode");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -421,25 +469,24 @@ GLSL_DEFINE(materialEmissionColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_CM_COL) ".xyz
|
|||
}
|
||||
|
||||
mstring_append(body,
|
||||
" oPos = tPosition * compositeMat;\n"
|
||||
" oPos.w = clampAwayZeroInf(oPos.w);\n"
|
||||
" oPos = invViewport * oPos;\n"
|
||||
" oPos = tPosition * compositeMat;\n"
|
||||
" oPos.z = oPos.z / clipRange.y;\n"
|
||||
" oPos.w = clampAwayZeroInf(oPos.w);\n"
|
||||
" oPos.xy /= oPos.w;\n"
|
||||
" oPos.xy += c[" stringify(NV_IGRAPH_XF_XFCTX_VPOFF) "].xy;\n"
|
||||
" oPos.xy = roundScreenCoords(oPos.xy);\n"
|
||||
" oPos.xy = (2.0f * oPos.xy - surfaceSize) / surfaceSize;\n"
|
||||
" oPos.xy *= oPos.w;\n"
|
||||
);
|
||||
|
||||
if (state->vulkan) {
|
||||
mstring_append(body, " oPos.y *= -1;\n");
|
||||
}
|
||||
|
||||
/* FIXME: Testing */
|
||||
if (state->point_params_enable) {
|
||||
mstring_append_fmt(
|
||||
mstring_append_fmt(uniforms, "%sfloat pointParams[8];\n", u);
|
||||
mstring_append(
|
||||
body,
|
||||
" float d_e = length(position * modelViewMat0);\n"
|
||||
" oPts.x = 1/sqrt(%f + %f*d_e + %f*d_e*d_e) + %f;\n",
|
||||
state->point_params[0], state->point_params[1], state->point_params[2],
|
||||
state->point_params[6]);
|
||||
mstring_append_fmt(body, " oPts.x = min(oPts.x*%f + %f, 64.0) * %d;\n",
|
||||
state->point_params[3], state->point_params[7],
|
||||
" oPts.x = 1/sqrt(pointParams[0] + pointParams[1] * d_e + pointParams[2] * d_e * d_e) + pointParams[6];\n");
|
||||
mstring_append_fmt(body, " oPts.x = min(oPts.x * pointParams[3] + pointParams[7], 64.0) * %d;\n",
|
||||
state->surface_scale_factor);
|
||||
} else {
|
||||
mstring_append_fmt(body, " oPts.x = %f * %d;\n", state->point_size,
|
||||
|
|
|
@ -639,7 +639,7 @@ static const char* vsh_header =
|
|||
// Unfortunately mix() falls victim to the same handling of exceptional
|
||||
// (inf/NaN) handling as a multiply, so per-component comparisons are used
|
||||
// to guarantee HW behavior (anything * 0 must == 0).
|
||||
" vec4 zero_components = sign(src0) * sign(src1);\n"
|
||||
" vec4 zero_components = sign(NaNToOne(src0)) * sign(NaNToOne(src1));\n"
|
||||
" vec4 ret = src0 * src1;\n"
|
||||
" if (zero_components.x == 0.0) { ret.x = 0.0; }\n"
|
||||
" if (zero_components.y == 0.0) { ret.y = 0.0; }\n"
|
||||
|
@ -821,22 +821,12 @@ void pgraph_gen_vsh_prog_glsl(uint16_t version,
|
|||
assert(has_final);
|
||||
|
||||
mstring_append(body,
|
||||
/* the shaders leave the result in screen space, while
|
||||
* opengl expects it in clip space.
|
||||
* TODO: the pixel-center co-ordinate differences should handled
|
||||
/* The shaders leave the result in screen space, while OpenGL expects it
|
||||
* in clip space.
|
||||
*/
|
||||
" oPos.x = 2.0 * (oPos.x - surfaceSize.x * 0.5) / surfaceSize.x;\n"
|
||||
);
|
||||
" oPos.xy = roundScreenCoords(oPos.xy);\n"
|
||||
" oPos.xy = (2.0f * oPos.xy - surfaceSize) / surfaceSize;\n"
|
||||
|
||||
if (vulkan) {
|
||||
mstring_append(body,
|
||||
" oPos.y = 2.0 * oPos.y / surfaceSize.y - 1.0;\n");
|
||||
} else {
|
||||
mstring_append(body, " oPos.y = -2.0 * (oPos.y - surfaceSize.y * 0.5) "
|
||||
"/ surfaceSize.y;\n");
|
||||
}
|
||||
|
||||
mstring_append(body,
|
||||
" oPos.z = oPos.z / clipRange.y;\n"
|
||||
" oPos.w = clampAwayZeroInf(oPos.w);\n"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -82,6 +81,17 @@ MString *pgraph_gen_vsh_glsl(const ShaderState *state, bool prefix_outputs)
|
|||
" t = clamp(t, uintBitsToFloat(0xDF800000), uintBitsToFloat(0x9F800000));\n"
|
||||
" }\n"
|
||||
" return t;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"vec4 NaNToOne(vec4 src) {\n"
|
||||
" return mix(src, vec4(1.0), isnan(src));\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
// Xbox NV2A rasterizer appears to have 4 bit precision fixed-point
|
||||
// fractional part and to convert floating-point coordinates by
|
||||
// by truncating (not flooring).
|
||||
"vec2 roundScreenCoords(vec2 pos) {\n"
|
||||
" return trunc(pos * 16.0f) / 16.0f;\n"
|
||||
"}\n");
|
||||
|
||||
pgraph_get_glsl_vtx_header(header, state->vulkan, state->smooth_shading,
|
||||
|
@ -129,6 +139,7 @@ MString *pgraph_gen_vsh_glsl(const ShaderState *state, bool prefix_outputs)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
mstring_append(header, "\n");
|
||||
|
||||
MString *body = mstring_from_str("void main() {\n");
|
||||
|
@ -233,19 +244,17 @@ MString *pgraph_gen_vsh_glsl(const ShaderState *state, bool prefix_outputs)
|
|||
break;
|
||||
}
|
||||
|
||||
mstring_append(body, " oFog.xyzw = vec4(fogFactor);\n");
|
||||
mstring_append(body, " oFog = NaNToOne(vec4(fogFactor));\n");
|
||||
} else {
|
||||
/* FIXME: Is the fog still calculated / passed somehow?!
|
||||
*/
|
||||
mstring_append(body, " oFog.xyzw = vec4(1.0);\n");
|
||||
mstring_append(body, " oFog = vec4(1.0);\n");
|
||||
}
|
||||
|
||||
/* 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"
|
||||
" vtxD0 = clamp(NaNToOne(oD0), 0.0, 1.0);\n"
|
||||
" vtxB0 = clamp(NaNToOne(oB0), 0.0, 1.0);\n"
|
||||
" vtxFog = oFog.x;\n"
|
||||
" vtxT0 = oT0;\n"
|
||||
" vtxT1 = oT1;\n"
|
||||
|
@ -254,6 +263,25 @@ 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(NaNToOne(oD1), 0.0, 1.0);\n"
|
||||
" vtxB1 = clamp(NaNToOne(oB1), 0.0, 1.0);\n"
|
||||
);
|
||||
|
||||
if (state->ignore_specular_alpha) {
|
||||
mstring_append(body,
|
||||
" vtxD1.w = 1.0;\n"
|
||||
" vtxB1.w = 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"
|
||||
|
|
|
@ -27,6 +27,7 @@ DEF_METHOD(NV097, SET_COMBINER_SPECULAR_FOG_CW0)
|
|||
DEF_METHOD(NV097, SET_COMBINER_SPECULAR_FOG_CW1)
|
||||
DEF_METHOD_CASE_4(NV097, SET_TEXTURE_ADDRESS, 64)
|
||||
DEF_METHOD(NV097, SET_CONTROL0)
|
||||
DEF_METHOD(NV097, SET_LIGHT_CONTROL)
|
||||
DEF_METHOD(NV097, SET_COLOR_MATERIAL)
|
||||
DEF_METHOD(NV097, SET_FOG_MODE)
|
||||
DEF_METHOD(NV097, SET_FOG_GEN_MODE)
|
||||
|
@ -78,6 +79,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)
|
||||
|
@ -94,6 +96,7 @@ DEF_METHOD_RANGE(NV097, SET_FOG_PARAMS, 3)
|
|||
DEF_METHOD_RANGE(NV097, SET_TEXGEN_PLANE_S, 4*4*4)
|
||||
DEF_METHOD(NV097, SET_TEXGEN_VIEW_MODEL)
|
||||
DEF_METHOD_RANGE(NV097, SET_FOG_PLANE, 4)
|
||||
DEF_METHOD_RANGE(NV097, SET_SPECULAR_PARAMS, 6)
|
||||
DEF_METHOD_RANGE(NV097, SET_SCENE_AMBIENT_COLOR, 3)
|
||||
DEF_METHOD_RANGE(NV097, SET_VIEWPORT_OFFSET, 4)
|
||||
DEF_METHOD_RANGE(NV097, SET_POINT_PARAMS, 8)
|
||||
|
@ -133,6 +136,11 @@ DEF_METHOD_RANGE(NV097, SET_TEXCOORD3_2F, 2)
|
|||
DEF_METHOD_RANGE(NV097, SET_TEXCOORD3_4F, 4)
|
||||
DEF_METHOD_RANGE(NV097, SET_TEXCOORD3_2S, 1)
|
||||
DEF_METHOD_RANGE(NV097, SET_TEXCOORD3_4S, 2)
|
||||
DEF_METHOD(NV097, SET_FOG_COORD)
|
||||
DEF_METHOD(NV097, SET_WEIGHT1F)
|
||||
DEF_METHOD_RANGE(NV097, SET_WEIGHT2F, 2)
|
||||
DEF_METHOD_RANGE(NV097, SET_WEIGHT3F, 3)
|
||||
DEF_METHOD_RANGE(NV097, SET_WEIGHT4F, 4)
|
||||
DEF_METHOD_RANGE(NV097, SET_VERTEX_DATA_ARRAY_FORMAT, 16)
|
||||
DEF_METHOD_RANGE(NV097, SET_VERTEX_DATA_ARRAY_OFFSET, 16)
|
||||
DEF_METHOD(NV097, SET_LOGIC_OP_ENABLE)
|
||||
|
@ -176,6 +184,7 @@ DEF_METHOD(NV097, CLEAR_SURFACE)
|
|||
DEF_METHOD(NV097, SET_CLEAR_RECT_HORIZONTAL)
|
||||
DEF_METHOD(NV097, SET_CLEAR_RECT_VERTICAL)
|
||||
DEF_METHOD_RANGE(NV097, SET_SPECULAR_FOG_FACTOR, 2)
|
||||
DEF_METHOD_RANGE(NV097, SET_SPECULAR_PARAMS_BACK, 6)
|
||||
DEF_METHOD(NV097, SET_SHADER_CLIP_PLANE_MODE)
|
||||
DEF_METHOD_RANGE(NV097, SET_COMBINER_COLOR_OCW, 8)
|
||||
DEF_METHOD(NV097, SET_COMBINER_CONTROL)
|
|
@ -19,7 +19,9 @@
|
|||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../nv2a_int.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "hw/xbox/nv2a/nv2a_int.h"
|
||||
#include "ui/xemu-notifications.h"
|
||||
#include "ui/xemu-settings.h"
|
||||
#include "util.h"
|
||||
|
@ -431,7 +433,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 +487,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 +506,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
|
||||
|
@ -1075,6 +1077,18 @@ DEF_METHOD(NV097, SET_CONTROL0)
|
|||
z_perspective);
|
||||
}
|
||||
|
||||
DEF_METHOD(NV097, SET_LIGHT_CONTROL)
|
||||
{
|
||||
PG_SET_MASK(NV_PGRAPH_CSV0_C, NV_PGRAPH_CSV0_C_SEPARATE_SPECULAR,
|
||||
(parameter & NV097_SET_LIGHT_CONTROL_SEPARATE_SPECULAR) != 0);
|
||||
|
||||
PG_SET_MASK(NV_PGRAPH_CSV0_C, NV_PGRAPH_CSV0_C_LOCALEYE,
|
||||
(parameter & NV097_SET_LIGHT_CONTROL_LOCALEYE) != 0);
|
||||
|
||||
PG_SET_MASK(NV_PGRAPH_CSV0_C, NV_PGRAPH_CSV0_C_ALPHA_FROM_MATERIAL_SPECULAR,
|
||||
(parameter & NV097_SET_LIGHT_CONTROL_ALPHA_FROM_MATERIAL_SPECULAR) != 0);
|
||||
}
|
||||
|
||||
DEF_METHOD(NV097, SET_COLOR_MATERIAL)
|
||||
{
|
||||
PG_SET_MASK(NV_PGRAPH_CSV0_C, NV_PGRAPH_CSV0_C_EMISSION,
|
||||
|
@ -1619,6 +1633,11 @@ DEF_METHOD(NV097, SET_MATERIAL_ALPHA)
|
|||
pg->material_alpha = *(float*)¶meter;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -1786,6 +1805,113 @@ DEF_METHOD_INC(NV097, SET_FOG_PLANE)
|
|||
pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_FOG] = true;
|
||||
}
|
||||
|
||||
struct CurveCoefficients {
|
||||
float a;
|
||||
float b;
|
||||
float c;
|
||||
};
|
||||
|
||||
static const struct CurveCoefficients curve_coefficients[] = {
|
||||
{1.000108475163, -9.838607076280, 54.829089549713},
|
||||
{1.199164441703, -3.292603784852, 7.799987995214},
|
||||
{8.653441252033, 29.189473787191, 43.586027561823},
|
||||
{-531.307758450301, 117.398468683934, 113.155490738338},
|
||||
{-4.662713151292, 1.221108944572, 1.217360986939},
|
||||
{-124.435242105211, 35.401219563514, 35.408114377045},
|
||||
{10672560.259502287954, 21565843.555823743343, 10894794.336297152564},
|
||||
{-51973801.463933646679, -104199997.554352939129, -52225454.356278456748},
|
||||
{972270.324080004124, 2025882.096547174733, 1054898.052467488218},
|
||||
};
|
||||
|
||||
static const float kCoefficient0StepPoints[] = {
|
||||
-0.022553957999, // power = 1.25
|
||||
-0.421539008617, // power = 4.00
|
||||
-0.678715527058, // power = 9.00
|
||||
-0.838916420937, // power = 20.00
|
||||
-0.961754500866, // power = 90.00
|
||||
-0.990773200989, // power = 375.00
|
||||
-0.994858562946, // power = 650.00
|
||||
-0.996561050415, // power = 1000.00
|
||||
-0.999547004700, // power = 1250.00
|
||||
};
|
||||
|
||||
static float reconstruct_quadratic(float c0, const struct CurveCoefficients *coefficients) {
|
||||
return coefficients->a + coefficients->b * c0 + coefficients->c * c0 * c0;
|
||||
}
|
||||
|
||||
static float reconstruct_saturation_growth_rate(float c0, const struct CurveCoefficients *coefficients) {
|
||||
return (coefficients->a * c0) / (coefficients->b + coefficients->c * c0);
|
||||
}
|
||||
|
||||
static float (* const reconstruct_func_map[])(float, const struct CurveCoefficients *) = {
|
||||
reconstruct_quadratic, // 1.0..1.25 max error 0.01 %
|
||||
reconstruct_quadratic, // 1.25..4.0 max error 2.2 %
|
||||
reconstruct_quadratic, // 4.0..9.0 max error 2.3 %
|
||||
reconstruct_saturation_growth_rate, // 9.0..20.0 max error 1.4 %
|
||||
reconstruct_saturation_growth_rate, // 20.0..90.0 max error 2.1 %
|
||||
reconstruct_saturation_growth_rate, // 90.0..375.0 max error 2.8%
|
||||
reconstruct_quadratic, // 375..650 max error 1.0 %
|
||||
reconstruct_quadratic, // 650..1000 max error 1.7%
|
||||
reconstruct_quadratic, // 1000..1250 max error 1.0%
|
||||
};
|
||||
|
||||
static float reconstruct_specular_power(const float *params) {
|
||||
// See https://github.com/dracc/xgu/blob/db3172d8c983629f0dc971092981846da22438ae/xgux.h#L279
|
||||
|
||||
// Values < 1.0 will result in a positive c1 and (c2 - c0 * 2) will be very
|
||||
// close to the original value.
|
||||
if (params[1] > 0.0f && params[2] < 1.0f) {
|
||||
return params[2] - (params[0] * 2.0f);
|
||||
}
|
||||
|
||||
float c0 = params[0];
|
||||
float c3 = params[3];
|
||||
// FIXME: This handling is not correct, but is distinct without crashing.
|
||||
// It does not appear possible for a DirectX-generated value to be positive,
|
||||
// so while this differs from hardware behavior, it may be irrelevant in
|
||||
// practice.
|
||||
if (c0 > 0.0f || c3 > 0.0f) {
|
||||
return 0.0001f;
|
||||
}
|
||||
|
||||
float reconstructed_power = 0.f;
|
||||
for (uint32_t i = 0; i < sizeof(kCoefficient0StepPoints) / sizeof(kCoefficient0StepPoints[0]); ++i) {
|
||||
if (c0 > kCoefficient0StepPoints[i]) {
|
||||
reconstructed_power = reconstruct_func_map[i](c0, &curve_coefficients[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float reconstructed_half_power = 0.f;
|
||||
for (uint32_t i = 0; i < sizeof(kCoefficient0StepPoints) / sizeof(kCoefficient0StepPoints[0]); ++i) {
|
||||
if (c3 > kCoefficient0StepPoints[i]) {
|
||||
reconstructed_half_power = reconstruct_func_map[i](c3, &curve_coefficients[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The range can be extended beyond 1250 by using the half power params. This
|
||||
// will only work for DirectX generated values, arbitrary params could
|
||||
// erroneously trigger this.
|
||||
//
|
||||
// There are some very low power (~1) values that have inverted powers, but
|
||||
// they are easily identified by comparatively high c0 parameters.
|
||||
if (reconstructed_power == 0.f || (reconstructed_half_power > reconstructed_power && c0 < -0.1f)) {
|
||||
return reconstructed_half_power * 2.f;
|
||||
}
|
||||
|
||||
return reconstructed_power;
|
||||
}
|
||||
|
||||
DEF_METHOD_INC(NV097, SET_SPECULAR_PARAMS)
|
||||
{
|
||||
int slot = (method - NV097_SET_SPECULAR_PARAMS) / 4;
|
||||
pg->specular_params[slot] = *(float *)¶meter;
|
||||
if (slot == 5) {
|
||||
pg->specular_power = reconstruct_specular_power(pg->specular_params);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_METHOD_INC(NV097, SET_SCENE_AMBIENT_COLOR)
|
||||
{
|
||||
int slot = (method - NV097_SET_SCENE_AMBIENT_COLOR) / 4;
|
||||
|
@ -2005,6 +2131,26 @@ DEF_METHOD_INC(NV097, SET_VERTEX4F)
|
|||
}
|
||||
}
|
||||
|
||||
DEF_METHOD(NV097, SET_FOG_COORD)
|
||||
{
|
||||
VertexAttribute *attribute = &pg->vertex_attributes[NV2A_VERTEX_ATTR_FOG];
|
||||
pgraph_allocate_inline_buffer_vertices(pg, NV2A_VERTEX_ATTR_FOG);
|
||||
attribute->inline_value[0] = *(float*)¶meter;
|
||||
attribute->inline_value[1] = attribute->inline_value[0];
|
||||
attribute->inline_value[2] = attribute->inline_value[0];
|
||||
attribute->inline_value[3] = attribute->inline_value[0];
|
||||
}
|
||||
|
||||
DEF_METHOD(NV097, SET_WEIGHT1F)
|
||||
{
|
||||
VertexAttribute *attribute = &pg->vertex_attributes[NV2A_VERTEX_ATTR_WEIGHT];
|
||||
pgraph_allocate_inline_buffer_vertices(pg, NV2A_VERTEX_ATTR_WEIGHT);
|
||||
attribute->inline_value[0] = *(float*)¶meter;
|
||||
attribute->inline_value[1] = 0.f;
|
||||
attribute->inline_value[2] = 0.f;
|
||||
attribute->inline_value[3] = 1.f;
|
||||
}
|
||||
|
||||
DEF_METHOD_INC(NV097, SET_NORMAL3S)
|
||||
{
|
||||
int slot = (method - NV097_SET_NORMAL3S) / 4;
|
||||
|
@ -2139,7 +2285,6 @@ DEF_METHOD_INC(NV097, SET_TEXCOORD1_4F)
|
|||
SET_VERTEX_ATTRIBUTE_F(NV097_SET_TEXCOORD1_4F, NV2A_VERTEX_ATTR_TEXTURE1);
|
||||
}
|
||||
|
||||
|
||||
DEF_METHOD_INC(NV097, SET_TEXCOORD2_4F)
|
||||
{
|
||||
SET_VERTEX_ATTRIBUTE_F(NV097_SET_TEXCOORD2_4F, NV2A_VERTEX_ATTR_TEXTURE2);
|
||||
|
@ -2150,8 +2295,34 @@ DEF_METHOD_INC(NV097, SET_TEXCOORD3_4F)
|
|||
SET_VERTEX_ATTRIBUTE_F(NV097_SET_TEXCOORD3_4F, NV2A_VERTEX_ATTR_TEXTURE3);
|
||||
}
|
||||
|
||||
DEF_METHOD_INC(NV097, SET_WEIGHT4F)
|
||||
{
|
||||
SET_VERTEX_ATTRIBUTE_F(NV097_SET_WEIGHT4F, NV2A_VERTEX_ATTR_WEIGHT);
|
||||
}
|
||||
|
||||
#undef SET_VERTEX_ATTRIBUTE_F
|
||||
|
||||
DEF_METHOD_INC(NV097, SET_WEIGHT2F)
|
||||
{
|
||||
int slot = (method - NV097_SET_WEIGHT2F) / 4;
|
||||
VertexAttribute *attribute =
|
||||
&pg->vertex_attributes[NV2A_VERTEX_ATTR_WEIGHT];
|
||||
pgraph_allocate_inline_buffer_vertices(pg, NV2A_VERTEX_ATTR_WEIGHT);
|
||||
attribute->inline_value[slot] = *(float*)¶meter;
|
||||
attribute->inline_value[2] = 0.0f;
|
||||
attribute->inline_value[3] = 1.0f;
|
||||
}
|
||||
|
||||
DEF_METHOD_INC(NV097, SET_WEIGHT3F)
|
||||
{
|
||||
int slot = (method - NV097_SET_WEIGHT3F) / 4;
|
||||
VertexAttribute *attribute =
|
||||
&pg->vertex_attributes[NV2A_VERTEX_ATTR_WEIGHT];
|
||||
pgraph_allocate_inline_buffer_vertices(pg, NV2A_VERTEX_ATTR_WEIGHT);
|
||||
attribute->inline_value[slot] = *(float*)¶meter;
|
||||
attribute->inline_value[3] = 1.0f;
|
||||
}
|
||||
|
||||
#define SET_VERTEX_ATRIBUTE_TEX_2F(command, attr_index) \
|
||||
do { \
|
||||
int slot = (method - (command)) / 4; \
|
||||
|
@ -2521,7 +2692,11 @@ DEF_METHOD(NV097, DRAW_ARRAYS)
|
|||
int32_t count = GET_MASK(parameter, NV097_DRAW_ARRAYS_COUNT) + 1;
|
||||
|
||||
if (pg->inline_elements_length) {
|
||||
/* FIXME: Determine HW behavior for overflow case. */
|
||||
/* FIXME: HW throws an exception if the start index is > 0xFFFF. This
|
||||
* would prevent this assert from firing for any reasonable choice of
|
||||
* NV2A_MAX_BATCH_LENGTH (which must be larger to accommodate
|
||||
* NV097_INLINE_ARRAY anyway)
|
||||
*/
|
||||
assert((pg->inline_elements_length + count) < NV2A_MAX_BATCH_LENGTH);
|
||||
assert(!pg->draw_arrays_prevent_connect);
|
||||
|
||||
|
@ -2723,6 +2898,15 @@ DEF_METHOD_INC(NV097, SET_SPECULAR_FOG_FACTOR)
|
|||
pgraph_reg_w(pg, NV_PGRAPH_SPECFOGFACTOR0 + slot*4, parameter);
|
||||
}
|
||||
|
||||
DEF_METHOD_INC(NV097, SET_SPECULAR_PARAMS_BACK)
|
||||
{
|
||||
int slot = (method - NV097_SET_SPECULAR_PARAMS_BACK) / 4;
|
||||
pg->specular_params_back[slot] = *(float *)¶meter;
|
||||
if (slot == 5) {
|
||||
pg->specular_power_back = reconstruct_specular_power(pg->specular_params_back);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_METHOD(NV097, SET_SHADER_CLIP_PLANE_MODE)
|
||||
{
|
||||
pgraph_reg_w(pg, NV_PGRAPH_SHADERCLIPMODE, parameter);
|
||||
|
@ -3022,4 +3206,3 @@ void pgraph_pre_shutdown_wait(NV2AState *d)
|
|||
PGRAPHState *pg = &d->pgraph;
|
||||
pg->renderer->ops.pre_shutdown_wait(d);
|
||||
}
|
||||
|
||||
|
|
|
@ -197,6 +197,11 @@ typedef struct PGRAPHState {
|
|||
float light_local_position[NV2A_MAX_LIGHTS][3];
|
||||
float light_local_attenuation[NV2A_MAX_LIGHTS][3];
|
||||
|
||||
float specular_params[6];
|
||||
float specular_power;
|
||||
float specular_params_back[6];
|
||||
float specular_power_back;
|
||||
|
||||
float point_params[8];
|
||||
|
||||
VertexAttribute vertex_attributes[NV2A_VERTEXSHADER_ATTRIBUTES];
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
@ -90,6 +92,16 @@ ShaderState pgraph_get_shader_state(PGRAPHState *pg)
|
|||
pgraph_reg_r(pg, NV_PGRAPH_CSV0_C), NV_PGRAPH_CSV0_C_SPECULAR);
|
||||
}
|
||||
|
||||
state.separate_specular = GET_MASK(
|
||||
pgraph_reg_r(pg, NV_PGRAPH_CSV0_C), NV_PGRAPH_CSV0_C_SEPARATE_SPECULAR);
|
||||
state.ignore_specular_alpha = !GET_MASK(
|
||||
pgraph_reg_r(pg, NV_PGRAPH_CSV0_C), NV_PGRAPH_CSV0_C_ALPHA_FROM_MATERIAL_SPECULAR);
|
||||
state.local_eye = GET_MASK(
|
||||
pgraph_reg_r(pg, NV_PGRAPH_CSV0_C), NV_PGRAPH_CSV0_C_LOCALEYE);
|
||||
|
||||
state.specular_power = pg->specular_power;
|
||||
state.specular_power_back = pg->specular_power_back;
|
||||
|
||||
/* vertex program stuff */
|
||||
state.vertex_program = vertex_program,
|
||||
state.z_perspective = pgraph_reg_r(pg, NV_PGRAPH_CONTROL_0) &
|
||||
|
|
|
@ -79,10 +79,17 @@ typedef struct ShaderState {
|
|||
enum MaterialColorSource diffuse_src;
|
||||
enum MaterialColorSource specular_src;
|
||||
|
||||
bool separate_specular;
|
||||
bool ignore_specular_alpha;
|
||||
bool local_eye;
|
||||
float specular_power;
|
||||
float specular_power_back;
|
||||
|
||||
bool lighting;
|
||||
enum VshLight light[NV2A_MAX_LIGHTS];
|
||||
|
||||
bool fixed_function;
|
||||
bool specular_enable;
|
||||
|
||||
/* vertex program */
|
||||
bool vertex_program;
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_RENDERDOC
|
||||
#include "trace/control.h"
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||
#include "thirdparty/renderdoc_app.h"
|
||||
#endif
|
||||
|
@ -46,11 +48,21 @@ void pgraph_vk_debug_frame_terminator(void)
|
|||
|
||||
PGRAPHVkState *r = g_nv2a->pgraph.vk_renderer_state;
|
||||
if (rdoc_api->IsTargetControlConnected()) {
|
||||
if (rdoc_api->IsFrameCapturing()) {
|
||||
bool capturing = rdoc_api->IsFrameCapturing();
|
||||
if (capturing && renderdoc_capture_frames == 0) {
|
||||
rdoc_api->EndFrameCapture(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(r->instance), 0);
|
||||
if (renderdoc_trace_frames) {
|
||||
trace_enable_events("-nv2a_pgraph_*");
|
||||
renderdoc_trace_frames = false;
|
||||
}
|
||||
}
|
||||
if (renderdoc_capture_frames > 0) {
|
||||
rdoc_api->StartFrameCapture(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(r->instance), 0);
|
||||
if (!capturing) {
|
||||
if (renderdoc_trace_frames) {
|
||||
trace_enable_events("nv2a_pgraph_*");
|
||||
}
|
||||
rdoc_api->StartFrameCapture(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(r->instance), 0);
|
||||
}
|
||||
--renderdoc_capture_frames;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -179,7 +179,6 @@ typedef struct ShaderBinding {
|
|||
int vsh_constant_loc;
|
||||
uint32_t vsh_constants[NV2A_VERTEXSHADER_CONSTANTS][4];
|
||||
|
||||
int inv_viewport_loc;
|
||||
int ltctxa_loc;
|
||||
int ltctxb_loc;
|
||||
int ltc1_loc;
|
||||
|
@ -190,9 +189,10 @@ typedef struct ShaderBinding {
|
|||
int light_infinite_direction_loc[NV2A_MAX_LIGHTS];
|
||||
int light_local_position_loc[NV2A_MAX_LIGHTS];
|
||||
int light_local_attenuation_loc[NV2A_MAX_LIGHTS];
|
||||
int specular_power_loc;
|
||||
int point_params_loc;
|
||||
|
||||
int clip_region_loc;
|
||||
|
||||
int material_alpha_loc;
|
||||
|
||||
int uniform_attrs_loc;
|
||||
|
|
|
@ -283,8 +283,6 @@ static void update_shader_constant_locations(ShaderBinding *binding)
|
|||
binding->fog_param_loc =
|
||||
uniform_index(&binding->vertex->uniforms, "fogParam");
|
||||
|
||||
binding->inv_viewport_loc =
|
||||
uniform_index(&binding->vertex->uniforms, "invViewport");
|
||||
binding->ltctxa_loc = uniform_index(&binding->vertex->uniforms, "ltctxa");
|
||||
binding->ltctxb_loc = uniform_index(&binding->vertex->uniforms, "ltctxb");
|
||||
binding->ltc1_loc = uniform_index(&binding->vertex->uniforms, "ltc1");
|
||||
|
@ -308,11 +306,17 @@ static void update_shader_constant_locations(ShaderBinding *binding)
|
|||
binding->clip_region_loc =
|
||||
uniform_index(&binding->fragment->uniforms, "clipRegion");
|
||||
|
||||
binding->point_params_loc =
|
||||
uniform_index(&binding->vertex->uniforms, "pointParams");
|
||||
|
||||
binding->material_alpha_loc =
|
||||
uniform_index(&binding->vertex->uniforms, "material_alpha");
|
||||
|
||||
binding->uniform_attrs_loc =
|
||||
uniform_index(&binding->vertex->uniforms, "inlineValue");
|
||||
|
||||
binding->specular_power_loc =
|
||||
uniform_index(&binding->vertex->uniforms, "specularPower");
|
||||
}
|
||||
|
||||
static void shader_cache_entry_init(Lru *lru, LruNode *node, void *state)
|
||||
|
@ -607,25 +611,9 @@ static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding,
|
|||
}
|
||||
}
|
||||
|
||||
/* estimate the viewport by assuming it matches the surface ... */
|
||||
unsigned int aa_width = 1, aa_height = 1;
|
||||
pgraph_apply_anti_aliasing_factor(pg, &aa_width, &aa_height);
|
||||
|
||||
float m11 = 0.5 * (pg->surface_binding_dim.width / aa_width);
|
||||
float m22 = -0.5 * (pg->surface_binding_dim.height / aa_height);
|
||||
float m33 = zmax;
|
||||
float m41 = *(float *)&pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPOFF][0];
|
||||
float m42 = *(float *)&pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPOFF][1];
|
||||
|
||||
float invViewport[16] = {
|
||||
1.0 / m11, 0, 0, 0, 0, 1.0 / m22, 0,
|
||||
0, 0, 0, 1.0 / m33, 0, -1.0 + m41 / m11, 1.0 + m42 / m22,
|
||||
0, 1.0
|
||||
};
|
||||
|
||||
if (binding->inv_viewport_loc != -1) {
|
||||
uniformMatrix4fv(&binding->vertex->uniforms,
|
||||
binding->inv_viewport_loc, &invViewport[0]);
|
||||
if (binding->specular_power_loc != -1) {
|
||||
uniform1f(&binding->vertex->uniforms, binding->specular_power_loc,
|
||||
pg->specular_power);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -712,6 +700,11 @@ static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding,
|
|||
uniform1iv(&binding->fragment->uniforms, binding->clip_region_loc,
|
||||
8 * 4, (void *)clip_regions);
|
||||
|
||||
if (binding->point_params_loc != -1) {
|
||||
uniform1iv(&binding->vertex->uniforms, binding->point_params_loc,
|
||||
ARRAY_SIZE(pg->point_params), (void *)pg->point_params);
|
||||
}
|
||||
|
||||
if (binding->material_alpha_loc != -1) {
|
||||
uniform1f(&binding->vertex->uniforms, binding->material_alpha_loc,
|
||||
pg->material_alpha);
|
||||
|
|
|
@ -55,8 +55,6 @@ enum VshFoggen {
|
|||
FOGGEN_RADIAL,
|
||||
FOGGEN_PLANAR,
|
||||
FOGGEN_ABS_PLANAR,
|
||||
FOGGEN_ERROR4,
|
||||
FOGGEN_ERROR5,
|
||||
FOGGEN_FOG_X
|
||||
};
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -1085,6 +1085,9 @@ extern const VMStateInfo vmstate_info_qlist;
|
|||
#define VMSTATE_UINT32_SUB_ARRAY(_f, _s, _start, _num) \
|
||||
VMSTATE_SUB_ARRAY(_f, _s, _start, _num, 0, vmstate_info_uint32, uint32_t)
|
||||
|
||||
#define VMSTATE_UINT32_SUB_ARRAY_V(_f, _s, _start, _num, _v) \
|
||||
VMSTATE_SUB_ARRAY(_f, _s, _start, _num, _v, vmstate_info_uint32, uint32_t)
|
||||
|
||||
#define VMSTATE_UINT32_2DARRAY(_f, _s, _n1, _n2) \
|
||||
VMSTATE_UINT32_2DARRAY_V(_f, _s, _n1, _n2, 0)
|
||||
|
||||
|
|
|
@ -3772,6 +3772,7 @@ if have_system
|
|||
'hw/remote',
|
||||
'hw/xbox/nv2a',
|
||||
'hw/xbox/mcpx',
|
||||
'hw/xbox/mcpx/dsp',
|
||||
'hw/xbox',
|
||||
]
|
||||
endif
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
#!/usr/bin/env python
|
||||
# /// script
|
||||
# dependencies = ["requests"]
|
||||
# ///
|
||||
"""
|
||||
Update Meson wrap file `revision` fields to point to latest release.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import argparse
|
||||
import configparser
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass, asdict
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
SEMVER_RE = re.compile(
|
||||
r"""
|
||||
^v?
|
||||
(?P<major>0|[1-9]\d*)\.
|
||||
(?P<minor>0|[1-9]\d*)\.
|
||||
(?P<patch>0|[1-9]\d*)
|
||||
$""",
|
||||
re.VERBOSE,
|
||||
)
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
WRAP_DIR = ROOT / "subprojects"
|
||||
SESSION = requests.Session()
|
||||
GH_TOKEN = os.getenv("GH_TOKEN", "")
|
||||
if GH_TOKEN:
|
||||
SESSION.headers["Authorization"] = f"Bearer {GH_TOKEN}"
|
||||
SESSION.headers["Accept"] = "application/vnd.github+json"
|
||||
|
||||
|
||||
def gh_sha_for_tag(owner: str, repo: str, tag: str) -> str:
|
||||
data = SESSION.get(
|
||||
f"https://api.github.com/repos/{owner}/{repo}/git/ref/tags/{tag}", timeout=30
|
||||
).json()
|
||||
|
||||
# First level: get the object it points to
|
||||
obj_type = data["object"]["type"]
|
||||
obj_sha = data["object"]["sha"]
|
||||
|
||||
if obj_type == "commit":
|
||||
# Lightweight tag
|
||||
return obj_sha
|
||||
elif obj_type == "tag":
|
||||
# Annotated tag: need to dereference
|
||||
tag_obj_url = data["object"]["url"]
|
||||
tag_data = requests.get(tag_obj_url).json()
|
||||
return tag_data["object"]["sha"]
|
||||
else:
|
||||
raise Exception(f"Unknown object type: {obj_type}")
|
||||
|
||||
|
||||
def gh_latest_release(
|
||||
owner: str, repo: str, pattern: re.Pattern
|
||||
) -> None | tuple[str, str]:
|
||||
"""
|
||||
Return (tag_name, commit_sha) for the most recent matching release.
|
||||
"""
|
||||
releases = SESSION.get(
|
||||
f"https://api.github.com/repos/{owner}/{repo}/releases", timeout=30
|
||||
).json()
|
||||
viable = [t for t in releases if pattern.match(t["tag_name"])]
|
||||
|
||||
if not viable:
|
||||
return None
|
||||
|
||||
tag_name = viable[0]["tag_name"]
|
||||
sha = gh_sha_for_tag(owner, repo, tag_name)
|
||||
|
||||
return tag_name, sha
|
||||
|
||||
|
||||
def gh_latest_tag(owner: str, repo: str, pattern: re.Pattern) -> tuple[str, str]:
|
||||
"""
|
||||
Return (tag_name, commit_sha) for the most recent matching tag.
|
||||
"""
|
||||
tags = SESSION.get(
|
||||
f"https://api.github.com/repos/{owner}/{repo}/tags", timeout=30
|
||||
).json()
|
||||
viable = [t for t in tags if pattern.match(t["name"])]
|
||||
|
||||
if not viable:
|
||||
return None
|
||||
|
||||
return viable[0]["name"], viable[0]["commit"]["sha"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class UpdatedWrap:
|
||||
path: str
|
||||
owner: str
|
||||
repo: str
|
||||
old_rev: str
|
||||
new_rev: str
|
||||
new_tag: str
|
||||
|
||||
|
||||
def update_wrap(path: Path) -> None | UpdatedWrap:
|
||||
"""
|
||||
Return (tag_name, commit_sha) if updated, otherwise None.
|
||||
"""
|
||||
cp = configparser.ConfigParser(interpolation=None)
|
||||
cp.read(path, encoding="utf-8")
|
||||
|
||||
if "wrap-git" not in cp:
|
||||
# FIXME: Support wrap-file from wrapdb
|
||||
return None
|
||||
|
||||
w = cp["wrap-git"]
|
||||
url = w.get("url", "")
|
||||
rev = w.get("revision", "").strip()
|
||||
m = re.match(r".*github\.com[:/](?P<owner>[^/]+)/(?P<repo>[^/.]+)(?:\.git)?", url)
|
||||
if not (m and rev):
|
||||
return None
|
||||
|
||||
owner, repo = m.group("owner"), m.group("repo")
|
||||
try:
|
||||
pattern = cp.get("update", "tag_regex", fallback=None)
|
||||
pattern = re.compile(pattern) if pattern else SEMVER_RE
|
||||
|
||||
latest = gh_latest_release(owner, repo, pattern)
|
||||
if latest is None:
|
||||
log.info("Couldn't find latest release for %s/%s", owner, repo)
|
||||
log.info("Searching for tags directly...")
|
||||
latest = gh_latest_tag(owner, repo, pattern)
|
||||
if latest is None:
|
||||
log.info("Couldn't find latest tag for %s/%s", owner, repo)
|
||||
return None
|
||||
tag, sha = latest
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
return None
|
||||
|
||||
if sha.startswith(rev):
|
||||
log.info("%s already at %s (%s)", path.name, tag, sha)
|
||||
return None
|
||||
|
||||
log.info("%s updated to %s (%s)", path.name, tag, sha)
|
||||
|
||||
w["revision"] = sha
|
||||
|
||||
with open(path, "w", encoding="utf-8") as file:
|
||||
cp.write(file)
|
||||
|
||||
# XXX: ConfigParser writes two extra newlines. Trim the last one.
|
||||
file.seek(file.tell() - 1, 0)
|
||||
file.truncate()
|
||||
|
||||
return UpdatedWrap(str(path), owner, repo, rev, sha, tag)
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument(
|
||||
"--manifest",
|
||||
"-m",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Print JSON-formatted updated manifest",
|
||||
)
|
||||
ap.add_argument(
|
||||
"wraps", nargs="*", help="Which wraps to update, or all if unspecified"
|
||||
)
|
||||
args = ap.parse_args()
|
||||
|
||||
wraps = args.wraps
|
||||
if wraps:
|
||||
wraps = [Path(p) for p in wraps]
|
||||
else:
|
||||
wraps = WRAP_DIR.glob("*.wrap")
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
updated = []
|
||||
for wrap in wraps:
|
||||
info = update_wrap(wrap)
|
||||
if info:
|
||||
updated.append(asdict(info))
|
||||
|
||||
if args.manifest:
|
||||
json.dump(updated, sys.stdout, indent=2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,4 +1,7 @@
|
|||
[wrap-git]
|
||||
url=https://github.com/KhronosGroup/SPIRV-Reflect
|
||||
revision=vulkan-sdk-1.4.304.0
|
||||
depth=1
|
||||
url = https://github.com/KhronosGroup/SPIRV-Reflect
|
||||
revision = c6c0f5c9796bdef40c55065d82e0df67c38a29a4
|
||||
depth = 1
|
||||
|
||||
[update]
|
||||
tag_regex = ^(vulkan-sdk-)?[\d\.]+$
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[wrap-git]
|
||||
url=https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator
|
||||
revision=v3.2.1
|
||||
depth=1
|
||||
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator
|
||||
revision = 1d8f600fd424278486eade7ed3e877c99f0846b1
|
||||
depth = 1
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
[wrap-git]
|
||||
url=https://github.com/KhronosGroup/glslang
|
||||
revision=vulkan-sdk-1.4.304.0
|
||||
depth=1
|
||||
url = https://github.com/KhronosGroup/glslang
|
||||
revision = fc9889c889561c5882e83819dcaffef5ed45529b
|
||||
depth = 1
|
||||
|
||||
[update]
|
||||
tag_regex = ^(vulkan-sdk-)?[\d\.]+$
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[wrap-git]
|
||||
url=https://github.com/marzer/tomlplusplus
|
||||
revision=c635f218c0aefc801d9748841930365e54fe3089
|
||||
depth=1
|
||||
url = https://github.com/marzer/tomlplusplus
|
||||
revision = 30172438cee64926dc41fdd9c11fb3ba5b2ba9de
|
||||
depth = 1
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[wrap-git]
|
||||
url=https://github.com/zeux/volk
|
||||
revision=1.4.304
|
||||
depth=1
|
||||
url = https://github.com/zeux/volk
|
||||
revision = 0b17a763ba5643e32da1b2152f8140461b3b7345
|
||||
depth = 1
|
||||
|
|
|
@ -86,3 +86,4 @@ subdir('qapi-schema')
|
|||
subdir('qtest')
|
||||
subdir('migration')
|
||||
subdir('functional')
|
||||
subdir('xbox')
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
all: basic
|
||||
|
||||
%: %.a56
|
||||
a56 -o $@ $<
|
|
@ -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
|
|
@ -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
|
|
@ -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'])
|
|
@ -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();
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
subdir('dsp')
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -490,7 +490,7 @@ void xemu_input_update_sdl_controller_state(ControllerState *state)
|
|||
|
||||
void xemu_input_update_rumble(ControllerState *state)
|
||||
{
|
||||
if (!state->rumble_enabled) {
|
||||
if (!state->rumble_enabled || !g_config.input.allow_vibration) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -485,6 +485,8 @@ void MainMenuInputView::Draw()
|
|||
SectionTitle("Options");
|
||||
Toggle("Auto-bind controllers", &g_config.input.auto_bind,
|
||||
"Bind newly connected controllers to any open port");
|
||||
Toggle("Controller vibration", &g_config.input.allow_vibration,
|
||||
"Allows the controllers to vibrate");
|
||||
Toggle("Background controller input capture",
|
||||
&g_config.input.background_input_capture,
|
||||
"Capture even if window is unfocused (requires restart)");
|
||||
|
|
|
@ -218,7 +218,7 @@ void xemu_hud_render(void)
|
|||
|
||||
#if defined(CONFIG_RENDERDOC)
|
||||
if (g_capture_renderdoc_frame) {
|
||||
nv2a_dbg_renderdoc_capture_frames(1);
|
||||
nv2a_dbg_renderdoc_capture_frames(1, false);
|
||||
g_capture_renderdoc_frame = false;
|
||||
}
|
||||
#endif
|
||||
|
@ -290,8 +290,10 @@ void xemu_hud_render(void)
|
|||
(ImGui::IsMouseClicked(ImGuiMouseButton_Right) &&
|
||||
!ImGui::IsAnyItemFocused() && !ImGui::IsAnyItemHovered())) {
|
||||
g_scene_mgr.PushScene(g_popup_menu);
|
||||
} else if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
||||
xemu_toggle_fullscreen();
|
||||
}
|
||||
|
||||
|
||||
bool mod_key_down = ImGui::IsKeyDown(ImGuiKey_ModShift);
|
||||
for (int f_key = 0; f_key < 4; ++f_key) {
|
||||
if (ImGui::IsKeyPressed((enum ImGuiKey)(ImGuiKey_F5 + f_key))) {
|
||||
|
|
|
@ -73,7 +73,9 @@ void ProcessKeyboardShortcuts(void)
|
|||
|
||||
#ifdef CONFIG_RENDERDOC
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_F10) && nv2a_dbg_renderdoc_available()) {
|
||||
nv2a_dbg_renderdoc_capture_frames(1);
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
int num_frames = io.KeyShift ? 5 : 1;
|
||||
nv2a_dbg_renderdoc_capture_frames(num_frames, io.KeyCtrl);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue