Compare commits

..

No commits in common. "master" and "CI-4204640" have entirely different histories.

105 changed files with 2206 additions and 4544 deletions

112
.github/labeler.yml vendored
View file

@ -1,111 +1,75 @@
# Labels are in alphabetical order.
cmake:
- changed-files:
- any-glob-to-any-file:
- 'CMake*'
- '**/CMakeLists.txt'
- '**/*.cmake'
- 'CMake*'
- '**/CMakeLists.txt'
- '**/*.cmake'
cpu-emulation:
- changed-files:
- any-glob-to-any-file:
- 'src/devices/x86/**'
- 'src/devices/x86/**'
deployment:
- changed-files:
- any-glob-to-any-file:
- '*.yml'
- '.github/workflows/CI.yml'
- '*.yml'
- '.github/workflows/CI.yml'
file-system:
- changed-files:
- any-glob-to-any-file:
- 'src/core/kernel/support/EmuFile*'
- 'src/core/kernel/support/EmuFile*'
graphics:
- changed-files:
- any-glob-to-any-file:
- 'src/core/hle/D3D8/**'
- 'src/core/hle/XGRAPHIC/**'
- 'src/devices/video/**'
- 'src/gui/*Video*'
- 'src/core/hle/D3D8/**'
- 'src/core/hle/XGRAPHIC/**'
- 'src/devices/video/**'
- 'src/gui/*Video*'
HLE:
- changed-files:
- any-glob-to-any-file:
- 'src/core/hle/**'
- 'src/core/kernel/**'
- 'src/core/hle/**'
- 'src/core/kernel/**'
informational:
- changed-files:
- any-glob-to-any-file:
- '**/*Logging*'
- '**/*Logging*/**'
- '**/README.md'
- '**/*Logging*'
- '**/*Logging*/**'
- '**/README.md'
input:
- changed-files:
- any-glob-to-any-file:
- 'src/common/input/**'
- 'src/core/hle/XAPI/input/**'
- 'src/devices/usb/**'
- 'src/gui/controllers/**'
- 'src/gui/*Input*'
- 'src/common/input/**'
- 'src/core/hle/XAPI/input/**'
- 'src/devices/usb/**'
- 'src/gui/controllers/**'
- 'src/gui/*Input*'
kernel:
- changed-files:
- any-glob-to-any-file:
- 'src/core/kernel/**'
- 'src/core/kernel/**'
LLE:
- changed-files:
- any-glob-to-any-file:
- 'src/devices/**'
- 'src/devices/**'
memory:
- changed-files:
- any-glob-to-any-file:
- 'src/core/kernel/memory-manager/**'
- 'src/core/kernel/memory-manager/**'
networking:
- changed-files:
- any-glob-to-any-file:
- 'src/core/hle/XONLINE/**'
- 'src/devices/network/**'
- 'src/gui/*Network*'
- 'src/core/hle/XONLINE/**'
- 'src/devices/network/**'
- 'src/gui/*Network*'
sound:
- changed-files:
- any-glob-to-any-file:
- 'src/common/audio/**'
- 'src/core/hle/DSOUND/**'
- 'src/core/hle/XACTENG/**'
- 'src/devices/audio/**'
- 'src/gui/*Audio*'
- 'src/common/audio/**'
- 'src/core/hle/DSOUND/**'
- 'src/core/hle/XACTENG/**'
- 'src/devices/audio/**'
- 'src/gui/*Audio*'
modules:
- changed-files:
- any-glob-to-any-file:
- 'import/**'
- 'import/**'
threading:
- changed-files:
- any-glob-to-any-file:
- 'src/core/kernel/support/EmuFS*'
- 'src/core/kernel/support/EmuFS*'
timing:
- changed-files:
- any-glob-to-any-file:
- 'src/common/Timer*'
- 'src/common/Timer*'
user interface:
- changed-files:
- any-glob-to-any-file:
- 'src/core/common/imgui/*'
- 'src/gui/**'
- 'src/core/common/imgui/*'
- 'src/gui/**'
xbdm:
- changed-files:
- any-glob-to-any-file:
- 'src/common/xbdm/**'
- 'src/common/xbdm/**'

View file

@ -30,21 +30,19 @@ jobs:
fail-fast: false
matrix:
configuration: [Release, Debug]
vsver: [2019]
winver: [7]
sdkver: [10.0.22621.0]
vsver: [2022]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Generate CMake files
run: cmake -B build -A Win32,version=${{ matrix.sdkver }} -DCMAKE_SYSTEM_VERSION=${{ matrix.winver }}
run: cmake -B build -A Win32
- name: Build
run: cmake --build build --config ${{ matrix.configuration }} -j $env:NUMBER_OF_PROCESSORS
- name: Prepare artifacts
if: matrix.configuration == 'Release'
run: cmake --install build --config ${{ matrix.configuration }} --prefix artifacts
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v3
if: matrix.configuration == 'Release'
with:
name: CxbxReloaded-${{ matrix.configuration }}-VS${{ matrix.vsver }}
@ -59,9 +57,9 @@ jobs:
needs: build-windows
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Download artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v3
with:
path: artifacts
- name: Re-zip artifacts
@ -74,7 +72,7 @@ jobs:
exit 1
fi
done
echo "tag_name=CI-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
echo "::set-output name=tag_name::CI-${GITHUB_SHA::7}"
- name: Create Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -7,9 +7,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Automatically close issues that don't follow the issue template
uses: ergo720/auto-close-issues@v1
uses: ergo720/auto-close-issues@v1.0.4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
issue-close-message: "@${issue.user.login}: your issue has been automatically closed because it does not follow the issue template." # optional property

View file

@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Labeler
uses: actions/labeler@v5
uses: actions/labeler@v4
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
sync-labels: true

6
.gitmodules vendored
View file

@ -1,6 +1,6 @@
[submodule "import/subhook"]
path = import/subhook
url = https://github.com/Cxbx-Reloaded/subhook.git
url = https://github.com/Zeex/subhook.git
shallow = true
[submodule "import/cs_x86"]
path = import/cs_x86
@ -43,7 +43,3 @@
[submodule "import/nv2a_vsh_cpu"]
path = import/nv2a_vsh_cpu
url = https://github.com/abaire/nv2a_vsh_cpu.git
[submodule "import/mio"]
path = import/mio
url = https://github.com/mandreyel/mio.git
shadow = true

View file

@ -22,8 +22,6 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/XbSymbolDatabase")
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/SDL2" EXCLUDE_FROM_ALL)
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/mio" EXCLUDE_FROM_ALL)
# Cxbx-Reloaded projects
set(CXBXR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR})
@ -79,6 +77,7 @@ file (GLOB CXBXR_HEADER_COMMON
"${CXBXR_ROOT_DIR}/src/common/util/cliConfig.hpp"
"${CXBXR_ROOT_DIR}/src/common/util/cliConverter.hpp"
"${CXBXR_ROOT_DIR}/src/common/util/CPUID.h"
"${CXBXR_ROOT_DIR}/src/common/util/crc32c.h"
"${CXBXR_ROOT_DIR}/src/common/util/CxbxUtil.h"
"${CXBXR_ROOT_DIR}/src/common/util/std_extend.hpp"
"${CXBXR_ROOT_DIR}/src/common/util/strConverter.hpp"
@ -145,7 +144,6 @@ file (GLOB CXBXR_HEADER_EMU
"${CXBXR_ROOT_DIR}/src/core/common/video/RenderBase.hpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/CxbxPixelShaderTemplate.hlsl"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/CxbxVertexShaderTemplate.hlsl"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/CxbxVertexShaderPassthrough.hlsl"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/Direct3D9.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsl"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsli"
@ -154,7 +152,7 @@ file (GLOB CXBXR_HEADER_EMU
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/PixelShader.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/Shader.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShader.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderCache.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderSource.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/WalkIndexBuffer.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/FixedFunctionState.h"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/ResourceTracker.h"
@ -254,7 +252,9 @@ file (GLOB CXBXR_SOURCE_COMMON
"${CXBXR_ROOT_DIR}/src/common/Settings.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/cliConfig.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/cliConverter.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/crc32c.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/CxbxUtil.cpp"
"${CXBXR_ROOT_DIR}/src/common/util/hasher.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/EmuShared.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/InlineFunc.cpp"
"${CXBXR_ROOT_DIR}/src/common/win32/IPCWindows.cpp"
@ -327,7 +327,7 @@ file (GLOB CXBXR_SOURCE_EMU
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/Shader.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/TextureStates.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShader.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderCache.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderSource.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/WalkIndexBuffer.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/FixedFunctionState.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/ResourceTracker.cpp"
@ -348,7 +348,6 @@ file (GLOB CXBXR_SOURCE_EMU
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/common/XbInternalDSVoice.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/common/XbInternalStruct.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/Intercept.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/JVS/JVS.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/Patches.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/XACTENG/XactEng.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/XGRAPHIC/XGraphic.cpp"
@ -379,8 +378,6 @@ file (GLOB CXBXR_SOURCE_EMU
"${CXBXR_ROOT_DIR}/src/core/kernel/support/NativeHandle.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/support/PatchRdtsc.cpp"
"${CXBXR_ROOT_DIR}/src/devices/ADM1032Device.cpp"
"${CXBXR_ROOT_DIR}/src/devices/Chihiro/JvsIO.cpp"
"${CXBXR_ROOT_DIR}/src/devices/Chihiro/MediaBoard.cpp"
"${CXBXR_ROOT_DIR}/src/devices/EEPROMDevice.cpp"
"${CXBXR_ROOT_DIR}/src/devices/network/NVNetDevice.cpp"
"${CXBXR_ROOT_DIR}/src/devices/MCPXDevice.cpp"
@ -474,19 +471,14 @@ install(FILES ${cxbxr_INSTALL_files}
DESTINATION bin
)
# Copy HLSL files to the output directory, which are loaded at runtime
set(CXBXR_HLSL_FILES ${CXBXR_HEADER_EMU})
list(FILTER CXBXR_HLSL_FILES INCLUDE REGEX ".*/src/core/hle/D3D8/Direct3D9/[^/]+\.hlsli?")
add_custom_command(
TARGET misc-batch POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CXBXR_HLSL_FILES} "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl"
# These files can be edited.
# Create backup copies for convenience of restoring original shader behaviour.
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl/backup
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CXBXR_HLSL_FILES} "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl/backup"
install(FILES
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/CxbxPixelShaderTemplate.hlsl"
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsl"
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsli"
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli"
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl"
DESTINATION bin/hlsl
)
install(DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>/hlsl DESTINATION bin)
set(cxbxr_GLEW_DLL "${CMAKE_SOURCE_DIR}/import/glew-2.0.0/bin/Release/Win32/glew32.dll")

View file

@ -17,7 +17,7 @@ Cxbx-Reloaded is an emulator for running Microsoft Xbox (and eventually, Chihiro
* [32-bit (x86) Visual C++ 2022 Redistributable](https://aka.ms/vs/17/release/vc_redist.x86.exe)
* [Npcap *(used for network emulation)*](https://nmap.org/npcap/#download)
* Make sure to enable winpcap compatibility mode.
* [WinUSB compliant driver](https://github.com/libusb/libusb/wiki/Windows#Driver_Installation)
* WinUSB compliant driver
* *Optional, only needed for USB pass-through of original Xbox and Steel Battalion controllers.*
### Wine

2
import/SDL2 vendored

@ -1 +1 @@
Subproject commit fa24d868ac2f8fd558e4e914c9863411245db8fd
Subproject commit b424665e0899769b200231ba943353a5fee1b6b6

@ -1 +1 @@
Subproject commit 774111351210e6f340246d6fb32741b09708f381
Subproject commit 75ce58fa8d135ef0a75bee729cde9542eda393b6

1
import/mio vendored

@ -1 +0,0 @@
Subproject commit 3f86a95c0784d73ce6815237ec33ed25f233b643

View file

@ -42,11 +42,8 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
LTC_NO_PRNGS
LTC_NO_MISC
LTC_NO_PROTOTYPES
# Enable Chihiro work
CHIHIRO_WORK
)
# Reference: https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically
add_compile_options(
# Catch synchronous (C++) exceptions only
@ -68,7 +65,7 @@ XXH_INLINE_ALL
)
file (GLOB RESOURCES
"${CXBXR_ROOT_DIR}/CONTRIBUTORS"
"${CXBXR_ROOT_DIR}/COPYING"
"${CXBXR_ROOT_DIR}/README.md"
@ -90,7 +87,7 @@ source_group(TREE ${CXBXR_ROOT_DIR}/import PREFIX import FILES
${CXBXR_SOURCE_EMU_IMPORT}
)
source_group(TREE ${CXBXR_ROOT_DIR}/src PREFIX source FILES
source_group(TREE ${CXBXR_ROOT_DIR}/src PREFIX source FILES
${CXBXR_SOURCE_GUIv1}
${CXBXR_SOURCE_COMMON}
)
@ -121,7 +118,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Reference: https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically
# /Zi = create a PDB file without affecting optimization
# /Ob3 = Controls inline expansion of functions.
# /Ob2 = Controls inline expansion of functions.
# /Oi = Generate intrinsic functions
# /Ot = In favor of using fast code than small code
# /GL = Whole program optimization
@ -132,7 +129,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Set optimization options for release build
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} \
/Zi \
/Ob3 \
/Ob2 \
/Oi \
/Ot \
/GL \
@ -142,7 +139,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
/Qpar \
"
)
# disable optimization for CxbxKrnl.cpp file
set_source_files_properties(
${CXBXR_KRNL_CPP} PROPERTIES COMPILE_FLAGS "/Od /GL-"
@ -150,7 +147,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
endif()
# Windows libraries
set(WINS_LIB
set(WINS_LIB
legacy_stdio_definitions
d3d9
d3dcompiler
@ -170,7 +167,6 @@ set(WINS_LIB
comctl32
XINPUT9_1_0
Iphlpapi
Dwmapi
)
target_link_libraries(cxbx
@ -180,7 +176,6 @@ target_link_libraries(cxbx
SDL2
imgui
libusb
mio::mio_min_winapi
${WINS_LIB}
)

View file

@ -48,9 +48,6 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Use inline XXHash version
XXH_INLINE_ALL
# Enable Chihiro work
CHIHIRO_WORK
)
add_compile_options(
/EHs
@ -131,7 +128,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Set optimization options for release build
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} \
/Zi \
/Ob3 \
/Ob2 \
/Oi \
/Ot \
/GL \
@ -144,7 +141,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
endif()
# Windows libraries
set(WINS_LIB
set(WINS_LIB
legacy_stdio_definitions
d3d9
d3dcompiler
@ -175,7 +172,6 @@ target_link_libraries(cxbxr-emu
imgui
libusb
nv2a_vsh_emulator
mio::mio_min_winapi
${WINS_LIB}
)

View file

@ -27,3 +27,14 @@ message("Runtime Build Directory: ${TargetRunTimeDir}")
# Copy glew32.dll to build type's folder.
set(CXBXR_GLEW_DLL "${CMAKE_SOURCE_DIR}/import/glew-2.0.0/bin/Release/Win32/glew32.dll")
file(COPY ${CXBXR_GLEW_DLL} DESTINATION ${TargetRunTimeDir})
# Copy certain HLSL files to the output directory, which we will load at runtime
set(CXBXR_HLSL_FILES
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli"
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl"
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsli"
"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionPixelShader.hlsl"
)
set(HlslOutputDir ${TargetRunTimeDir}/hlsl)
file(MAKE_DIRECTORY ${HlslOutputDir})
file(COPY ${CXBXR_HLSL_FILES} DESTINATION ${HlslOutputDir})

View file

@ -84,4 +84,6 @@ extern volatile bool g_bPrintfOn;
#define CxbxSetThreadName(Name)
#endif
#include <filesystem>
#endif

View file

@ -25,7 +25,6 @@
#define LOG_PREFIX CXBXR_MODULE::FILE
#define LOG_PREFIX_INIT CXBXR_MODULE::INIT
#include <filesystem>
#include "common/cxbxr.hpp"
#include "Settings.hpp"
#include "EmuShared.h"

View file

@ -31,8 +31,6 @@ extern std::string g_DataFilePath;
extern std::string g_DiskBasePath;
extern std::string g_MuBasePath;
#include <filesystem>
//TODO: Possible move CxbxResolveHostToFullPath inline function someplace else if become useful elsewhere.
// Let filesystem library clean it up for us, including resolve host's symbolic link path.
// Since internal kernel do translate to full path than preserved host symoblic link path.

View file

@ -49,9 +49,8 @@ void ipc_send_gui_update(IPC_UPDATE_GUI command, const unsigned int value);
// ******************************************************************
typedef enum class _IPC_UPDATE_KERNEL {
CONFIG_LOGGING_SYNC = 0,
CONFIG_INPUT_SYNC,
CONFIG_CHANGE_TIME
CONFIG_LOGGING_SYNC = 0
, CONFIG_INPUT_SYNC
} IPC_UPDATE_KERNEL;
void ipc_send_kernel_update(IPC_UPDATE_KERNEL command, const int value, const unsigned int hwnd);

View file

@ -26,6 +26,7 @@
// ******************************************************************
#ifndef SETTINGS_HPP
#define SETTINGS_HPP
#include "Cxbx.h"
#include "SimpleIni.h"
#include "common\input\InputManager.h"

View file

@ -25,45 +25,19 @@
// *
// ******************************************************************
#include <core\kernel\exports\xboxkrnl.h>
#ifdef _WIN32
#include <windows.h>
#endif
#include <thread>
#include <vector>
#include <mutex>
#include <array>
#include "Timer.h"
#include "common\util\CxbxUtil.h"
#include "core\kernel\support\EmuFS.h"
#include "core\kernel\exports\EmuKrnlPs.hpp"
#include "core\kernel\exports\EmuKrnl.h"
#include "devices\Xbox.h"
#include "devices\usb\OHCI.h"
#include "core\hle\DSOUND\DirectSound\DirectSoundGlobal.hpp"
static std::atomic_uint64_t last_qpc; // last time when QPC was called
static std::atomic_uint64_t exec_time; // total execution time in us since the emulation started
static uint64_t pit_last; // last time when the pit time was updated
static uint64_t pit_last_qpc; // last QPC time of the pit
// The frequency of the high resolution clock of the host, and the start time
int64_t HostQPCFrequency, HostQPCStartTime;
void timer_init()
{
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER *>(&HostQPCFrequency));
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *>(&HostQPCStartTime));
pit_last_qpc = last_qpc = HostQPCStartTime;
pit_last = get_now();
// Synchronize xbox system time with host time
LARGE_INTEGER HostSystemTime;
GetSystemTimeAsFileTime((LPFILETIME)&HostSystemTime);
xbox::KeSystemTime.High2Time = HostSystemTime.u.HighPart;
xbox::KeSystemTime.LowPart = HostSystemTime.u.LowPart;
xbox::KeSystemTime.High1Time = HostSystemTime.u.HighPart;
}
#include "core/kernel/exports/EmuKrnlPs.hpp"
#ifdef __linux__
#include <time.h>
#endif
// More precise sleep, but with increased CPU usage
void SleepPrecise(std::chrono::steady_clock::time_point targetTime)
@ -95,83 +69,174 @@ void SleepPrecise(std::chrono::steady_clock::time_point targetTime)
}
}
// NOTE: the pit device is not implemented right now, so we put this here
static uint64_t pit_next(uint64_t now)
// Virtual clocks will probably become useful once LLE CPU is implemented, but for now we don't need them.
// See the QEMUClockType QEMU_CLOCK_VIRTUAL of XQEMU for more info.
#define CLOCK_REALTIME 0
//#define CLOCK_VIRTUALTIME 1
// Vector storing all the timers created
static std::vector<TimerObject*> TimerList;
// The frequency of the high resolution clock of the host, and the start time
int64_t HostQPCFrequency, HostQPCStartTime;
// Lock to acquire when accessing TimerList
std::mutex TimerMtx;
// Returns the current time of the timer
uint64_t GetTime_NS(TimerObject* Timer)
{
constexpr uint64_t pit_period = 1000;
uint64_t next = pit_last + pit_period;
if (now >= next) {
xbox::KiClockIsr(now - pit_last);
pit_last = get_now();
return pit_period;
}
return pit_last + pit_period - now; // time remaining until next clock interrupt
#ifdef _WIN32
uint64_t Ret = Timer_GetScaledPerformanceCounter(SCALE_S_IN_NS);
#elif __linux__
static struct timespec ts;
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
uint64_t Ret = Muldiv64(ts.tv_sec, SCALE_S_IN_NS, 1) + ts.tv_nsec;
#else
#error "Unsupported OS"
#endif
return Ret;
}
static void update_non_periodic_events()
// Calculates the next expire time of the timer
static inline uint64_t GetNextExpireTime(TimerObject* Timer)
{
// update dsound
dsound_worker();
return GetTime_NS(Timer) + Timer->ExpireTime_MS.load();
}
// check for hw interrupts
for (int i = 0; i < MAX_BUS_INTERRUPT_LEVEL; i++) {
// If the interrupt is pending and connected, process it
if (g_bEnableAllInterrupts && HalSystemInterrupts[i].IsPending() && EmuInterruptList[i] && EmuInterruptList[i]->Connected) {
HalSystemInterrupts[i].Trigger(EmuInterruptList[i]);
// Deallocates the memory of the timer
void Timer_Destroy(TimerObject* Timer)
{
unsigned int index, i;
std::lock_guard<std::mutex>lock(TimerMtx);
index = TimerList.size();
for (i = 0; i < index; i++) {
if (Timer == TimerList[i]) {
index = i;
}
}
assert(index != TimerList.size());
delete Timer;
TimerList.erase(TimerList.begin() + index);
}
uint64_t get_now()
void Timer_Shutdown()
{
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
uint64_t elapsed_us = now.QuadPart - last_qpc;
last_qpc = now.QuadPart;
elapsed_us *= 1000000;
elapsed_us /= HostQPCFrequency;
exec_time += elapsed_us;
return exec_time;
unsigned i, iXboxThreads = 0;
TimerMtx.lock();
for (i = 0; i < TimerList.size(); i++) {
TimerObject* Timer = TimerList[i];
// We only need to terminate host threads.
if (!Timer->IsXboxTimer) {
Timer_Exit(Timer);
}
// If the thread is xbox, we need to increment for while statement check
else {
iXboxThreads++;
}
}
// Only perform wait for host threads, otherwise xbox threads are
// already handled within xbox kernel for shutdown process. See CxbxrKrnlSuspendThreads function.
int counter = 0;
while (iXboxThreads != TimerList.size()) {
if (counter >= 8) {
break;
}
TimerMtx.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
TimerMtx.lock();
counter++;
}
TimerList.clear();
TimerMtx.unlock();
}
static uint64_t get_next(uint64_t now)
// Thread that runs the timer
void NTAPI ClockThread(void *TimerArg)
{
std::array<uint64_t, 5> next = {
pit_next(now),
g_NV2A->vblank_next(now),
g_NV2A->ptimer_next(now),
g_USB0->m_HostController->OHCI_next(now),
dsound_next(now)
};
return *std::min_element(next.begin(), next.end());
}
TimerObject *Timer = static_cast<TimerObject *>(TimerArg);
if (!Timer->Name.empty()) {
CxbxSetThreadName(Timer->Name.c_str());
}
if (!Timer->IsXboxTimer) {
g_AffinityPolicy->SetAffinityOther();
}
xbox::void_xt NTAPI system_events(xbox::PVOID arg)
{
// Testing shows that, if this thread has the same priority of the other xbox threads, it can take tens, even hundreds of ms to complete a single loop.
// So we increase its priority to above normal, so that it scheduled more often
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
// Always run this thread at dpc level to prevent it from ever executing APCs/DPCs
xbox::KeRaiseIrqlToDpcLevel();
uint64_t NewExpireTime = GetNextExpireTime(Timer);
while (true) {
const uint64_t last_time = get_now();
const uint64_t nearest_next = get_next(last_time);
while (true) {
update_non_periodic_events();
uint64_t elapsed_us = get_now() - last_time;
if (elapsed_us >= nearest_next) {
break;
if (GetTime_NS(Timer) > NewExpireTime) {
if (Timer->Exit.load()) {
Timer_Destroy(Timer);
return;
}
std::this_thread::yield();
Timer->Callback(Timer->Opaque);
NewExpireTime = GetNextExpireTime(Timer);
}
Sleep(1); // prevent burning the cpu
}
}
// Changes the expire time of a timer
void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms)
{
Timer->ExpireTime_MS.store(Expire_ms);
}
// Destroys the timer
void Timer_Exit(TimerObject* Timer)
{
Timer->Exit.store(true);
}
// Allocates the memory for the timer object
TimerObject* Timer_Create(TimerCB Callback, void* Arg, std::string Name, bool IsXboxTimer)
{
std::lock_guard<std::mutex>lock(TimerMtx);
TimerObject* pTimer = new TimerObject;
pTimer->Type = CLOCK_REALTIME;
pTimer->Callback = Callback;
pTimer->ExpireTime_MS.store(0);
pTimer->Exit.store(false);
pTimer->Opaque = Arg;
pTimer->Name = Name.empty() ? "Unnamed thread" : std::move(Name);
pTimer->IsXboxTimer = IsXboxTimer;
TimerList.emplace_back(pTimer);
return pTimer;
}
// Starts the timer
// Expire_MS must be expressed in NS
void Timer_Start(TimerObject* Timer, uint64_t Expire_MS)
{
Timer->ExpireTime_MS.store(Expire_MS);
if (Timer->IsXboxTimer) {
xbox::HANDLE hThread;
CxbxrCreateThread(&hThread, xbox::zeroptr, ClockThread, Timer, FALSE);
}
else {
std::thread(ClockThread, Timer).detach();
}
}
// Retrives the frequency of the high resolution clock of the host
void Timer_Init()
{
#ifdef _WIN32
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&HostQPCFrequency));
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&HostQPCStartTime));
#elif __linux__
ClockFrequency = 0;
#else
#error "Unsupported OS"
#endif
}
int64_t Timer_GetScaledPerformanceCounter(int64_t Period)
{
LARGE_INTEGER currentQPC;

View file

@ -40,12 +40,33 @@
#define SCALE_MS_IN_US 1000
#define SCALE_US_IN_US 1
/* typedef of the timer object and the callback function */
typedef void(*TimerCB)(void*);
typedef struct _TimerObject
{
int Type; // timer type
std::atomic_uint64_t ExpireTime_MS; // when the timer expires (ms)
std::atomic_bool Exit; // indicates that the timer should be destroyed
TimerCB Callback; // function to call when the timer expires
void* Opaque; // opaque argument to pass to the callback
std::string Name; // the name of the timer thread (if any)
bool IsXboxTimer; // indicates that the timer should run on the Xbox CPU
}
TimerObject;
extern int64_t HostQPCFrequency;
void timer_init();
uint64_t get_now();
/* Timer exported functions */
TimerObject* Timer_Create(TimerCB Callback, void* Arg, std::string Name, bool IsXboxTimer);
void Timer_Start(TimerObject* Timer, uint64_t Expire_MS);
void Timer_Exit(TimerObject* Timer);
void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms);
uint64_t GetTime_NS(TimerObject* Timer);
void Timer_Init();
void Timer_Shutdown();
int64_t Timer_GetScaledPerformanceCounter(int64_t Period);
void SleepPrecise(std::chrono::steady_clock::time_point targetTime);
#endif

View file

@ -121,6 +121,9 @@ bool HandleFirstLaunch()
}
// NOTE: Require to be after g_renderbase's shutdown process.
// Next thing we need to do is shutdown our timer threads.
Timer_Shutdown();
// NOTE: Must be last step of shutdown process and before CxbxUnlockFilePath call!
// Shutdown the memory manager
g_VMManager.Shutdown();

View file

@ -24,6 +24,7 @@
// ******************************************************************
#pragma once
#include <filesystem>
#include <string>
#include <optional>

View file

@ -37,7 +37,6 @@
#include "DInputKeyboardMouse.h"
#include "InputManager.h"
#include "core\kernel\support\Emu.h"
#include <algorithm>
// Unfortunately, sdl doesn't seem to be able to capture keyboard/mouse input from windows it didn't create (we currently use
// win32 for that). So unless we create sdl windows, we will have to keep dinput around to handle keyboard/mouse input.

View file

@ -25,7 +25,6 @@
// *
// ******************************************************************
#include <algorithm>
#include "Button.h"
#include "InputManager.h"
#include "layout_xbox_device.h"

View file

@ -170,7 +170,7 @@ namespace Libusb
}
else {
for (size_t i = 0; i < ARRAY_SIZE(SupportedDevices_VidPid); ++i) {
if ((Desc->idVendor == SupportedDevices_VidPid[i][0]) && (Desc->idProduct == SupportedDevices_VidPid[i][1])) {
if ((Desc->idVendor = SupportedDevices_VidPid[i][0]) && (Desc->idProduct == SupportedDevices_VidPid[i][1])) {
m_Type = XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER;
m_UcType = XINPUT_DEVTYPE_GAMEPAD;
m_UcSubType = XINPUT_DEVSUBTYPE_GC_GAMEPAD;
@ -185,18 +185,9 @@ namespace Libusb
if (m_Type == XBOX_INPUT_DEVICE::DEVICE_INVALID) { return; }
// check if we are able to open device through libusb
if (int err = libusb_open(Dev, &m_hDev)) {
// Couldn't open device, create an error log report then don't use it.
EmuLog(LOG_LEVEL::ERROR2, "Unable to open original xbox device \"%s\" (%hX:%hX), libusb's error was: %s",
m_Name.c_str(), Desc->idVendor, Desc->idProduct, libusb_strerror(err));
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
return;
}
// If we are able to open device, continue with query process.
else {
// Duke, S and SBC have 1 configuration, 1 interface and 2 endpoints (input and output) and use the default alternate setting zero.
// The code below assumes that third-party controllers follow suit.
// Duke, S and SBC have 1 configuration, 1 interface and 2 endpoints (input and output) and use the default alternate setting zero.
// The code below assumes that third-party controllers follow suit.
if (libusb_open(Dev, &m_hDev) == 0) {
libusb_config_descriptor *ConfDesc;
if (libusb_get_active_config_descriptor(Dev, &ConfDesc) == 0) {
if (ConfDesc->bNumInterfaces == 1) {
@ -220,7 +211,7 @@ namespace Libusb
}
}
EmuLog(LOG_LEVEL::INFO, "Out endpoint %s", m_HasEndpointOut ? "present" : "not present");
if (int err = libusb_claim_interface(m_hDev, m_IfaceNum)) {
if (int err = libusb_claim_interface(m_hDev, m_IfaceNum) != 0) {
EmuLog(LOG_LEVEL::INFO, "Rejected device %s because libusb could not claim its interface. The error was: %s",
m_Name.c_str(), libusb_strerror(err));
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;

View file

@ -35,7 +35,6 @@
#define LOG_PREFIX CXBXR_MODULE::SDL
#include <assert.h>
#include <algorithm>
#include <thread>
#include "core\kernel\support\Emu.h"
#include "SdlJoystick.h"

View file

@ -26,15 +26,12 @@
#ifndef CXBXUTIL_H
#define CXBXUTIL_H
#include <algorithm>
#include <stdexcept>
#include "xbox_types.h"
#include "Cxbx.h"
#include <stdint.h>
#include <assert.h>
#include <string>
#include <type_traits>
#include <vector>
#include "std_extend.hpp" // for ARRAY_SIZE
/* This is a linux struct for vectored I/O. See readv() and writev() */

335
src/common/util/crc32c.cpp Normal file
View file

@ -0,0 +1,335 @@
/*
Copyright (c) 2013 - 2014, 2016 Mark Adler, Robert Vazan, Max Vysokikh
This software is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "crc32c.h"
#include <intrin.h>
#include <algorithm>
#define POLY 0x82f63b78
#define LONG_SHIFT 8192
#define SHORT_SHIFT 256
typedef const uint8_t *buffer;
static uint32_t table[16][256];
static uint32_t long_shifts[4][256];
static uint32_t short_shifts[4][256];
static bool _tableInitialized;
void calculate_table();
/* Table-driven software version as a fall-back. This is about 15 times slower
than using the hardware instructions. This assumes little-endian integers,
as is the case on Intel processors that the assembler code here is for. */
extern "C" CRC32C_API uint32_t crc32c_append_sw(uint32_t crci, buffer input, size_t length)
{
buffer next = input;
#ifdef _M_X64
uint64_t crc;
#else
uint32_t crc;
#endif
crc = crci ^ 0xffffffff;
#ifdef _M_X64
while (length && ((uintptr_t)next & 7) != 0)
{
crc = table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
--length;
}
while (length >= 16)
{
crc ^= *(uint64_t *)next;
uint64_t high = *(uint64_t *)(next + 8);
crc = table[15][crc & 0xff]
^ table[14][(crc >> 8) & 0xff]
^ table[13][(crc >> 16) & 0xff]
^ table[12][(crc >> 24) & 0xff]
^ table[11][(crc >> 32) & 0xff]
^ table[10][(crc >> 40) & 0xff]
^ table[9][(crc >> 48) & 0xff]
^ table[8][crc >> 56]
^ table[7][high & 0xff]
^ table[6][(high >> 8) & 0xff]
^ table[5][(high >> 16) & 0xff]
^ table[4][(high >> 24) & 0xff]
^ table[3][(high >> 32) & 0xff]
^ table[2][(high >> 40) & 0xff]
^ table[1][(high >> 48) & 0xff]
^ table[0][high >> 56];
next += 16;
length -= 16;
}
#else
while (length && ((uintptr_t)next & 3) != 0)
{
crc = table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
--length;
}
while (length >= 12)
{
crc ^= *(uint32_t *)next;
uint32_t high = *(uint32_t *)(next + 4);
uint32_t high2 = *(uint32_t *)(next + 8);
crc = table[11][crc & 0xff]
^ table[10][(crc >> 8) & 0xff]
^ table[9][(crc >> 16) & 0xff]
^ table[8][crc >> 24]
^ table[7][high & 0xff]
^ table[6][(high >> 8) & 0xff]
^ table[5][(high >> 16) & 0xff]
^ table[4][high >> 24]
^ table[3][high2 & 0xff]
^ table[2][(high2 >> 8) & 0xff]
^ table[1][(high2 >> 16) & 0xff]
^ table[0][high2 >> 24];
next += 12;
length -= 12;
}
#endif
while (length)
{
crc = table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
--length;
}
return (uint32_t)crc ^ 0xffffffff;
}
/* Apply the zeros operator table to crc. */
static inline uint32_t shift_crc(uint32_t shift_table[][256], uint32_t crc)
{
return shift_table[0][crc & 0xff]
^ shift_table[1][(crc >> 8) & 0xff]
^ shift_table[2][(crc >> 16) & 0xff]
^ shift_table[3][crc >> 24];
}
/* Compute CRC-32C using the Intel hardware instruction. */
extern "C" CRC32C_API uint32_t crc32c_append_hw(uint32_t crc, buffer buf, size_t len)
{
buffer next = buf;
buffer end;
#ifdef _M_X64
uint64_t crc0, crc1, crc2; /* need to be 64 bits for crc32q */
#else
uint32_t crc0, crc1, crc2;
#endif
/* pre-process the crc */
crc0 = crc ^ 0xffffffff;
/* compute the crc for up to seven leading bytes to bring the data pointer
to an eight-byte boundary */
while (len && ((uintptr_t)next & 7) != 0)
{
crc0 = _mm_crc32_u8(static_cast<uint32_t>(crc0), *next);
++next;
--len;
}
#ifdef _M_X64
/* compute the crc on sets of LONG_SHIFT*3 bytes, executing three independent crc
instructions, each on LONG_SHIFT bytes -- this is optimized for the Nehalem,
Westmere, Sandy Bridge, and Ivy Bridge architectures, which have a
throughput of one crc per cycle, but a latency of three cycles */
while (len >= 3 * LONG_SHIFT)
{
crc1 = 0;
crc2 = 0;
end = next + LONG_SHIFT;
do
{
crc0 = _mm_crc32_u64(crc0, *reinterpret_cast<const uint64_t *>(next));
crc1 = _mm_crc32_u64(crc1, *reinterpret_cast<const uint64_t *>(next + LONG_SHIFT));
crc2 = _mm_crc32_u64(crc2, *reinterpret_cast<const uint64_t *>(next + 2 * LONG_SHIFT));
next += 8;
} while (next < end);
crc0 = shift_crc(long_shifts, static_cast<uint32_t>(crc0)) ^ crc1;
crc0 = shift_crc(long_shifts, static_cast<uint32_t>(crc0)) ^ crc2;
next += 2 * LONG_SHIFT;
len -= 3 * LONG_SHIFT;
}
/* do the same thing, but now on SHORT_SHIFT*3 blocks for the remaining data less
than a LONG_SHIFT*3 block */
while (len >= 3 * SHORT_SHIFT)
{
crc1 = 0;
crc2 = 0;
end = next + SHORT_SHIFT;
do
{
crc0 = _mm_crc32_u64(crc0, *reinterpret_cast<const uint64_t *>(next));
crc1 = _mm_crc32_u64(crc1, *reinterpret_cast<const uint64_t *>(next + SHORT_SHIFT));
crc2 = _mm_crc32_u64(crc2, *reinterpret_cast<const uint64_t *>(next + 2 * SHORT_SHIFT));
next += 8;
} while (next < end);
crc0 = shift_crc(short_shifts, static_cast<uint32_t>(crc0)) ^ crc1;
crc0 = shift_crc(short_shifts, static_cast<uint32_t>(crc0)) ^ crc2;
next += 2 * SHORT_SHIFT;
len -= 3 * SHORT_SHIFT;
}
/* compute the crc on the remaining eight-byte units less than a SHORT_SHIFT*3
block */
end = next + (len - (len & 7));
while (next < end)
{
crc0 = _mm_crc32_u64(crc0, *reinterpret_cast<const uint64_t *>(next));
next += 8;
}
#else
/* compute the crc on sets of LONG_SHIFT*3 bytes, executing three independent crc
instructions, each on LONG_SHIFT bytes -- this is optimized for the Nehalem,
Westmere, Sandy Bridge, and Ivy Bridge architectures, which have a
throughput of one crc per cycle, but a latency of three cycles */
while (len >= 3 * LONG_SHIFT)
{
crc1 = 0;
crc2 = 0;
end = next + LONG_SHIFT;
do
{
crc0 = _mm_crc32_u32(crc0, *reinterpret_cast<const uint32_t *>(next));
crc1 = _mm_crc32_u32(crc1, *reinterpret_cast<const uint32_t *>(next + LONG_SHIFT));
crc2 = _mm_crc32_u32(crc2, *reinterpret_cast<const uint32_t *>(next + 2 * LONG_SHIFT));
next += 4;
} while (next < end);
crc0 = shift_crc(long_shifts, static_cast<uint32_t>(crc0)) ^ crc1;
crc0 = shift_crc(long_shifts, static_cast<uint32_t>(crc0)) ^ crc2;
next += 2 * LONG_SHIFT;
len -= 3 * LONG_SHIFT;
}
/* do the same thing, but now on SHORT_SHIFT*3 blocks for the remaining data less
than a LONG_SHIFT*3 block */
while (len >= 3 * SHORT_SHIFT)
{
crc1 = 0;
crc2 = 0;
end = next + SHORT_SHIFT;
do
{
crc0 = _mm_crc32_u32(crc0, *reinterpret_cast<const uint32_t *>(next));
crc1 = _mm_crc32_u32(crc1, *reinterpret_cast<const uint32_t *>(next + SHORT_SHIFT));
crc2 = _mm_crc32_u32(crc2, *reinterpret_cast<const uint32_t *>(next + 2 * SHORT_SHIFT));
next += 4;
} while (next < end);
crc0 = shift_crc(short_shifts, static_cast<uint32_t>(crc0)) ^ crc1;
crc0 = shift_crc(short_shifts, static_cast<uint32_t>(crc0)) ^ crc2;
next += 2 * SHORT_SHIFT;
len -= 3 * SHORT_SHIFT;
}
/* compute the crc on the remaining eight-byte units less than a SHORT_SHIFT*3
block */
end = next + (len - (len & 7));
while (next < end)
{
crc0 = _mm_crc32_u32(crc0, *reinterpret_cast<const uint32_t *>(next));
next += 4;
}
#endif
len &= 7;
/* compute the crc for up to seven trailing bytes */
while (len)
{
crc0 = _mm_crc32_u8(static_cast<uint32_t>(crc0), *next);
++next;
--len;
}
/* return a post-processed crc */
return static_cast<uint32_t>(crc0) ^ 0xffffffff;
}
extern "C" CRC32C_API int crc32c_hw_available()
{
int info[4];
__cpuid(info, 1);
return (info[2] & (1 << 20)) != 0;
}
void calculate_table()
{
for(int i = 0; i < 256; i++)
{
uint32_t res = (uint32_t)i;
for(int t = 0; t < 16; t++) {
for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
table[t][i] = res;
}
}
_tableInitialized = true;
}
void calculate_table_hw()
{
for(int i = 0; i < 256; i++)
{
uint32_t res = (uint32_t)i;
for (int k = 0; k < 8 * (SHORT_SHIFT - 4); k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
for(int t = 0; t < 4; t++) {
for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
short_shifts[3 - t][i] = res;
}
for (int k = 0; k < 8 * (LONG_SHIFT - 4 - SHORT_SHIFT); k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
for(int t = 0; t < 4; t++) {
for (int k = 0; k < 8; k++) res = (res & 1) == 1 ? POLY ^ (res >> 1) : (res >> 1);
long_shifts[3 - t][i] = res;
}
}
}
uint32_t (*append_func)(uint32_t, buffer, size_t);
void __crc32_init()
{
if (append_func == NULL)
{
// somebody can call sw version directly, so, precalculate table for this version
calculate_table();
if (crc32c_hw_available()) {
calculate_table_hw();
append_func = crc32c_append_hw;
} else {
append_func = crc32c_append_sw;
}
}
}
extern "C" CRC32C_API uint32_t crc32c_append(uint32_t crc, buffer input, size_t length)
{
if (append_func == NULL) {
__crc32_init();
}
return append_func(crc, input, length);
}

35
src/common/util/crc32c.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef CRC32C_H
#define CRC32C_H
#define CRC32C_API
#include <stdint.h>
/*
Computes CRC-32C (Castagnoli) checksum. Uses Intel's CRC32 instruction if it is available.
Otherwise it uses a very fast software fallback.
*/
extern "C" CRC32C_API uint32_t crc32c_append(
uint32_t crc, // Initial CRC value. Typically it's 0.
// You can supply non-trivial initial value here.
// Initial value can be used to chain CRC from multiple buffers.
const uint8_t *input, // Data to be put through the CRC algorithm.
size_t length); // Length of the data in the input buffer.
/*
Software fallback version of CRC-32C (Castagnoli) checksum.
*/
extern "C" CRC32C_API uint32_t crc32c_append_sw(uint32_t crc, const uint8_t *input, size_t length);
/*
Hardware version of CRC-32C (Castagnoli) checksum. Will fail, if CPU does not support related instructions. Use a crc32c_append version instead of.
*/
extern "C" CRC32C_API uint32_t crc32c_append_hw(uint32_t crc, const uint8_t *input, size_t length);
/*
Checks is hardware version of CRC-32C is available.
*/
extern "C" CRC32C_API int crc32c_hw_available();
#endif

View file

@ -0,0 +1,42 @@
#include "hasher.h"
#include "xxhash.h"
#include "crc32c.h"
#include <cstdio>
enum {
HASH_NONE = 0,
HASH_XXH3,
HASH_CRC32C
};
static int g_HashAlgorithm = HASH_NONE;
void InitHasher()
{
// Detect the best hashing algorithm to use for the host machine
// TODO/Future Improvement: This could be expanded to support even more hash algorithims
// And we could hash a random buffer to calculate the fastest hash to use on a given host
printf("Selecting hash algorithm: ");
if (crc32c_hw_available()) {
printf("CRC32C\n");
g_HashAlgorithm = HASH_CRC32C;
} else {
printf("XXH3\n");
g_HashAlgorithm = HASH_XXH3;
}
}
uint64_t ComputeHash(const void* data, size_t len)
{
if (g_HashAlgorithm == HASH_NONE) {
InitHasher();
}
switch (g_HashAlgorithm) {
case HASH_XXH3: return XXH3_64bits(data, len);
case HASH_CRC32C: return crc32c_append(0, (uint8_t*)data, len);
}
return 0;
}

View file

@ -27,7 +27,8 @@
#ifndef _HASHER_H
#define _HASHER_H
#include "xxhash.h"
#define ComputeHash XXH3_64bits
#include <stdint.h>
uint64_t ComputeHash(const void* data, size_t len);
#endif

View file

@ -101,10 +101,6 @@ void ipc_send_kernel_update(IPC_UPDATE_KERNEL command, const int value, const un
cmdParam = ID_SYNC_CONFIG_INPUT;
break;
case IPC_UPDATE_KERNEL::CONFIG_CHANGE_TIME:
cmdParam = ID_SYNC_TIME_CHANGE;
break;
default:
cmdParam = 0;
break;

View file

@ -72,7 +72,7 @@ Xbe::Xbe(const char *x_szFilename)
// This is necessary because CxbxInitWindow internally calls g_AffinityPolicy->SetAffinityOther. If we are launched directly from the command line and the dashboard
// cannot be opened, we will crash below because g_AffinityPolicy will be empty
g_AffinityPolicy = AffinityPolicy::InitPolicy();
CxbxInitWindow();
CxbxInitWindow(false);
ULONG FatalErrorCode = FATAL_ERROR_XBE_DASH_GENERIC;
@ -732,29 +732,25 @@ void Xbe::PurgeBadChar(std::string& s, const std::string& illegalChars)
}
}
const char *Xbe::GameRegionToString(uint32_t dwRegionFlags)
const char *Xbe::GameRegionToString()
{
if (!dwRegionFlags) {
dwRegionFlags = m_Certificate.dwGameRegion;
}
const char *Region_text[] = {
"Unknown", "NTSC", "JAPAN", "NTSC+JAPAN",
"PAL", "PAL+NTSC", "PAL+JAPAN", "Region Free",
"DEBUG", "NTSC (DEBUG)", "JAPAN (DEBUG)", "NTSC+JAPAN (DEBUG)",
"PAL (DEBUG)", "PAL+NTSC (DEBUG)", "PAL+JAPAN (DEBUG)", "Region Free (DEBUG)"
"Unknown", "NTSC", "JAP", "NTSC+JAP",
"PAL", "PAL+NTSC", "PAL+JAP", "Region Free",
"DEBUG", "NTSC (DEBUG)", "JAP (DEBUG)", "NTSC+JAP (DEBUG)",
"PAL (DEBUG)", "PAL+NTSC (DEBUG)", "PAL+JAP (DEBUG)", "Region Free (DEBUG)"
};
const uint32_t all_regions = XBEIMAGE_GAME_REGION_NA |
XBEIMAGE_GAME_REGION_JAPAN |
XBEIMAGE_GAME_REGION_RESTOFWORLD |
XBEIMAGE_GAME_REGION_MANUFACTURING;
if(dwRegionFlags & ~all_regions) {
if(m_Certificate.dwGameRegion & ~all_regions) {
return "REGION ERROR";
}
uint8_t index = (dwRegionFlags & XBEIMAGE_GAME_REGION_MANUFACTURING) ? 0x8 : 0;
index |= (dwRegionFlags & 0x7);
uint8_t index = (m_Certificate.dwGameRegion & XBEIMAGE_GAME_REGION_MANUFACTURING) ? 0x8 : 0;
index |= (m_Certificate.dwGameRegion & 0x7);
return Region_text[index];
}
@ -829,15 +825,5 @@ XbeType Xbe::GetXbeType()
return XbeType::xtRetail;
}
uint32_t Xbe::GetDiscVersion()
{
return m_Certificate.dwVersion & 0xFF;
}
uint32_t Xbe::GetPatchVersion()
{
return (m_Certificate.dwVersion & 0xFFFFFF00) >> 8;
}
template auto Xbe::FindSection<true>(const char *zsSectionName);
template auto Xbe::FindSection<false>(const char *zsSectionName);

View file

@ -81,13 +81,10 @@ class Xbe : public Error
void PurgeBadChar(std::string& s, const std::string& illegalChars = "\\/:?\"<>|");
// Convert game region field to string
const char *GameRegionToString(uint32_t dwRegionFlags = 0);
const char *GameRegionToString();
XbeType GetXbeType();
uint32_t GetDiscVersion();
uint32_t GetPatchVersion();
// Xbe header
#include "AlignPrefix1.h"
struct Header
@ -160,7 +157,7 @@ class Xbe : public Error
uint32_t dwAllowedMedia; // 0x009C - allowed media types
uint32_t dwGameRegion; // 0x00A0 - game region
uint32_t dwGameRatings; // 0x00A4 - game ratings
uint32_t dwDiscNumber; // 0x00A8 - disc number
uint32_t dwDiskNumber; // 0x00A8 - disk number
uint32_t dwVersion; // 0x00AC - version
uint8_t bzLanKey[16]; // 0x00B0 - lan key
uint8_t bzSignatureKey[16]; // 0x00C0 - signature key

View file

@ -58,7 +58,6 @@ std::string DumpInformation(Xbe* Xbe_object)
}
#define SSTREAM_SET_HEX(stream_name) stream_name << std::setfill('0') << std::uppercase << std::hex;
#define SSTREAM_SET_DEC(stream_name) stream_name << std::setfill('0') << std::uppercase << std::dec;
std::string FormatTitleId(uint32_t title_id)
{
@ -360,12 +359,8 @@ std::string XbePrinter::GenMediaInfo()
text << "Allowed Media : 0x" << std::setw(8) << Xbe_certificate->dwAllowedMedia << " (" << AllowedMediaToString() << ")\n";
text << "Game Region : 0x" << std::setw(8) << Xbe_certificate->dwGameRegion << " (" << Xbe_to_print->GameRegionToString() << ")\n";
text << "Game Ratings : 0x" << std::setw(8) << Xbe_certificate->dwGameRatings << " (" << GameRatingToString() << ")\n";
text << "Disc Number : 0x" << std::setw(8) << Xbe_certificate->dwDiscNumber << "\n";
text << "Version : 0x" << std::setw(8) << Xbe_certificate->dwVersion << "\n";
SSTREAM_SET_DEC(text);
text << "Disc Version : " << std::setw(0) << Xbe_to_print->GetDiscVersion() << "\n";
text << "Patch Version : " << std::setw(0) << Xbe_to_print->GetPatchVersion() << "\n";
text << "Disk Number : 0x" << std::setw(8) << Xbe_certificate->dwDiskNumber << "\n";
text << "Version : 1." << std::dec << std::setw(2) << Xbe_certificate->dwVersion << "\n";
return text.str();
}

View file

@ -1,3 +1,6 @@
// This starts the raw string (comment to get syntax highlighting, UNCOMMENT to compile) :
R"DELIMITER(
struct PS_INPUT // Declared identical to vertex shader output (see VS_OUTPUT)
{
float2 iPos : VPOS; // Screen space x,y pixel location
@ -51,8 +54,7 @@ uniform const float4 FC1 : register(c17); // Note : Maps to PSH_XBOX_CONSTANT_FC
uniform const float4 BEM[4] : register(c19); // Note : PSH_XBOX_CONSTANT_BEM for 4 texture stages
uniform const float4 LUM[4] : register(c23); // Note : PSH_XBOX_CONSTANT_LUM for 4 texture stages
uniform const float FRONTFACE_FACTOR : register(c27); // Note : PSH_XBOX_CONSTANT_LUM for 4 texture stages
uniform const float4 FOGINFO : register(c28);
uniform const float FOGENABLE : register(c29);
#define CM_LT(c) if(c < 0) clip(-1); // = PS_COMPAREMODE_[RSTQ]_LT
#define CM_GE(c) if(c >= 0) clip(-1); // = PS_COMPAREMODE_[RSTQ]_GE
@ -90,9 +92,10 @@ uniform const float FOGENABLE : register(c29);
#define PS_FINALCOMBINERSETTING_CLAMP_SUM
#endif
// Hardcoded state will be inserted here
// <HARDCODED STATE GOES HERE>
// End hardcoded state
)DELIMITER", /* This terminates the 1st raw string within the 16380 single-byte characters limit. // */
// See https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2026?f1url=%3FappId%3DDev15IDEF1%26l%3DEN-US%26k%3Dk(C2026)%26rd%3Dtrue&view=vs-2019
// Second raw string :
R"DELIMITER(
// PS_COMBINERCOUNT_UNIQUE_C0 steers whether for C0 to use combiner stage-specific constants c0_0 .. c0_7, or c0_0 for all stages
#ifdef PS_COMBINERCOUNT_UNIQUE_C0
@ -170,6 +173,10 @@ uniform const float FOGENABLE : register(c29);
// HLSL : https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-lerp
// lerp(x, y, s ) x*(1-s ) + y*s == x + s(y-x)
// lerp(s2, s1, s0) s2*(1-s0) + s1*s0
)DELIMITER", /* This terminates the 1st raw string within the 16380 single-byte characters limit. // */
// See https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2026?f1url=%3FappId%3DDev15IDEF1%26l%3DEN-US%26k%3Dk(C2026)%26rd%3Dtrue&view=vs-2019
// Second raw string :
R"DELIMITER(
float m21d(const float input)
{
@ -198,39 +205,20 @@ float m21(const float input)
return (float)tmp / 127; // -128 scales to -1.007874016, 0 scales to 0.0, 127 scales to 1.0
}
float hls(float input) // 0..65535 range
{
float tmp = (float)(input);
tmp = (input < 32768) ? tmp / 32767 : (tmp - 65536) / 32767; // -1..1
return (float)tmp;
}
float hlu(float input) // 0..65535 range
{
return (float)input / 65535; // 0..1
}
float p2(float input) // power of 2
{
return input * input;
}
// Note : each component seems already in range [0..1], but two must be combined into one
#define TwoIntoOne(a,b) (((a * 256) + b) * 255)
#define TwoIntoOne(a,b) (((a * 255) * 256) + (b * 255)) / 255 // TODO : Verify whether this works at all !
#define CalcHiLo(in) H = TwoIntoOne(in.x, in.y); L = TwoIntoOne(in.z, in.w) // TODO : Verify whether this works at all !
// Dot mappings over the output value of a (4 component 8 bit unsigned) texture stage register into a (3 component float) vector value, for use in a dot product calculation:
#define PS_DOTMAPPING_ZERO_TO_ONE(in) dm = in.rgb // :r8g8b8a8->(r,g,b): 0x00=>0, 0xff=>1 thus : output = (input / 0xff )
#define PS_DOTMAPPING_MINUS1_TO_1_D3D(in) dm = float3(m21d(in.x), m21d(in.y), m21d(in.z)) // :r8g8b8a8->(r,g,b): 0x00=>-128/127, 0x01=>-1, 0x80=>0, 0xff=>1 thus : output = ((input - 0x100 ) / 0x7f )
#define PS_DOTMAPPING_MINUS1_TO_1_GL(in) dm = float3(m21g(in.x), m21g(in.y), m21g(in.z)) // :r8g8b8a8->(r,g,b): 0x80=>-1, 0x00=>0, 0x7f=>1 thus : output = (input < 0x80 ) ? (input / 0x7f ) : ((input - 0x100 ) / 0x80 ) (see https://en.wikipedia.org/wiki/Two's_complement)
#define PS_DOTMAPPING_MINUS1_TO_1(in) dm = float3(m21( in.x), m21( in.y), m21( in.z)) // :r8g8b8a8->(r,g,b): 0x80=>-128/127, ?0x81=>-1, 0x00=>0, 0x7f=>1 thus : output = (input < 0x80 ) ? (input / 0x7f ) : ((input - 0x100 ) / 0x7f ) (see https://en.wikipedia.org/wiki/Two's_complement)
#define PS_DOTMAPPING_MINUS1_TO_1(in) dm = float3(m21(in.x), m21(in.y), m21(in.z)) // :r8g8b8a8->(r,g,b): 0x80=>-128/127, ?0x81=>-1, 0x00=>0, 0x7f=>1 thus : output = (input < 0x80 ) ? (input / 0x7f ) : ((input - 0x100 ) / 0x7f ) (see https://en.wikipedia.org/wiki/Two's_complement)
#define PS_DOTMAPPING_HILO_1(in) CalcHiLo(in); dm = float3(hlu(H), hlu(L), 1) // :H16L16 ->(H,L,1): 0x0000=>0, 0xffff=>1 thus : output = (input / 0xffff)
#define PS_DOTMAPPING_HILO_HEMISPHERE_D3D(in) CalcHiLo(in); dm = float3(hls(H), hls(L), sqrt(1-p2(H)-p2(L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=32767/32768 thus : output = ((input - 0x10000) / 0x7fff)
#define PS_DOTMAPPING_HILO_HEMISPHERE_GL(in) CalcHiLo(in); dm = float3(hls(H), hls(L), sqrt(1-p2(H)-p2(L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x8000)
#define PS_DOTMAPPING_HILO_HEMISPHERE(in) CalcHiLo(in); dm = float3(hls(H), hls(L), sqrt(1-p2(H)-p2(L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)): 0x8000=>-32768/32767, 0x8001=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x7fff)
#define PS_DOTMAPPING_HILO_1(in) CalcHiLo(in); dm = float3(H, L, 1) // :H16L16 ->(H,L,1): 0x0000=>0, 0xffff=>1 thus : output = (input / 0xffff)
#define PS_DOTMAPPING_HILO_HEMISPHERE_D3D(in) CalcHiLo(in); dm = float3(H, L, sqrt(1-(H*H)-(L*L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=32767/32768 thus : output = ((input - 0x10000) / 0x7fff)
#define PS_DOTMAPPING_HILO_HEMISPHERE_GL(in) CalcHiLo(in); dm = float3(H, L, sqrt(1-(H*H)-(L*L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)):? 0x8000=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x8000)
#define PS_DOTMAPPING_HILO_HEMISPHERE(in) CalcHiLo(in); dm = float3(H, L, sqrt(1-(H*H)-(L*L))) // :H16L16 ->(H,L,sqrt(1-H^2-L^2)): 0x8000=>-32768/32767, 0x8001=>-1, 0x0000=>0, 0x7fff=>1 thus : output = (input < 0x8000) ? (input / 0x7fff) : ((input - 0x10000) / 0x7fff)
// Declare one sampler per each {Sampler Type, Texture Stage} combination
// TODO : Generate sampler status?
@ -324,7 +312,7 @@ float3 DoBumpEnv(const float4 TexCoord, const float4 BumpEnvMat, const float4 sr
/*--23 texbrdf */ #define PS_TEXTUREMODES_BRDF(ts) s = Brdf(ts); v = Sample3D(ts, s); t[ts] = v // TODO : Test (t[ts-2] is 16 bit eyePhi,eyeSigma; t[ts-1] is lightPhi,lightSigma)
/*--23 texm3x2tex */ #define PS_TEXTUREMODES_DOT_ST(ts) CalcDot(ts); n = Normal2(ts); s = n; v = Sample2D(ts, s); t[ts] = v // TODO : Test
/*--23 texm3x2depth */ #define PS_TEXTUREMODES_DOT_ZW(ts) CalcDot(ts); n = Normal2(ts); if (n.y==0) v=1;else v = n.x / n.y; t[ts] = v // TODO : Make depth-check use result of division, but how?
/*--2- texm3x3diff */ #define PS_TEXTUREMODES_DOT_RFLCT_DIFF(ts) CalcDot(ts); n = Normal2(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
/*--2- texm3x3diff */ #define PS_TEXTUREMODES_DOT_RFLCT_DIFF(ts) CalcDot(ts); n = Normal3(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
/*---3 texm3x3vspec */ #define PS_TEXTUREMODES_DOT_RFLCT_SPEC(ts) CalcDot(ts); n = Normal3(ts); s = Reflect(n, Eye); v = Sample6F(ts, s); t[ts] = v // TODO : Test
/*---3 texm3x3tex */ #define PS_TEXTUREMODES_DOT_STR_3D(ts) CalcDot(ts); n = Normal3(ts); s = n; v = Sample3D(ts, s); t[ts] = v // TODO : Test
/*---3 texm3x3tex */ #define PS_TEXTUREMODES_DOT_STR_CUBE(ts) CalcDot(ts); n = Normal3(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
@ -337,34 +325,6 @@ float3 DoBumpEnv(const float4 TexCoord, const float4 BumpEnvMat, const float4 sr
PS_OUTPUT main(const PS_INPUT xIn)
{
// fogging
const float fogDepth = xIn.iFog.x; // Don't abs this value! Test-case : DolphinClassic xdk sampl
const int fogTableMode = FOGINFO.x;
const float fogDensity = FOGINFO.y;
const float fogStart = FOGINFO.z;
const float fogEnd = FOGINFO.w;
const int FOG_TABLE_NONE = 0;
const int FOG_TABLE_EXP = 1;
const int FOG_TABLE_EXP2 = 2;
const int FOG_TABLE_LINEAR = 3;
float fogFactor;
if(FOGENABLE == 0){
fogFactor = 1;
}
else{
if(fogTableMode == FOG_TABLE_NONE)
fogFactor = fogDepth;
if(fogTableMode == FOG_TABLE_EXP)
fogFactor = 1 / exp(fogDepth * fogDensity); // 1 / e^(d * density)
if(fogTableMode == FOG_TABLE_EXP2)
fogFactor = 1 / exp(pow(fogDepth * fogDensity, 2)); // 1 / e^((d * density)^2)
if(fogTableMode == FOG_TABLE_LINEAR)
fogFactor = (fogEnd - fogDepth) / (fogEnd - fogStart);
}
// Local constants
const float4 zero = 0;
const float4 half = 0.5; // = s_negbias(zero)
@ -398,11 +358,12 @@ PS_OUTPUT main(const PS_INPUT xIn)
// Note : VFACE/FrontFace has been unreliable, investigate again if some test-case shows bland colors
v0 = isFrontFace ? xIn.iD0 : xIn.iB0; // Diffuse front/back
v1 = isFrontFace ? xIn.iD1 : xIn.iB1; // Specular front/back
fog = float4(c_fog.rgb, saturate(fogFactor)); // color from PSH_XBOX_CONSTANT_FOG, alpha from vertex shader output / pixel shader input
fog = float4(c_fog.rgb, xIn.iFog); // color from PSH_XBOX_CONSTANT_FOG, alpha from vertex shader output / pixel shader input
// Xbox shader program will be inserted here
// <XBOX SHADER PROGRAM GOES HERE>
// End Xbox shader program
// Xbox shader program
)DELIMITER", /* This terminates the 2nd raw string within the 16380 single-byte characters limit. // */
// Third and last raw string, the footer :
R"DELIMITER(
// Copy r0.rgba to output
PS_OUTPUT xOut;
@ -411,3 +372,5 @@ PS_OUTPUT main(const PS_INPUT xIn)
return xOut;
}
// End of pixel shader footer)DELIMITER" /* This terminates the footer raw string" // */

View file

@ -1,108 +0,0 @@
// Xbox HLSL pretransformed vertex shader
// Default values for vertex registers, and whether to use them
uniform float4 vRegisterDefaultValues[16] : register(c192);
uniform float4 vRegisterDefaultFlagsPacked[4] : register(c208);
uniform float4 xboxScreenspaceScale : register(c212);
uniform float4 xboxScreenspaceOffset : register(c213);
uniform float4 xboxTextureScale[4] : register(c214);
// Parameters for mapping the shader's fog output value to a fog factor
uniform float4 CxbxFogInfo: register(c218); // = CXBX_D3DVS_CONSTREG_FOGINFO
struct VS_INPUT
{
float4 v[16] : TEXCOORD;
};
// Output registers
struct VS_OUTPUT
{
float4 oPos : POSITION; // Homogeneous clip space position
float4 oD0 : COLOR0; // Primary color (front-facing)
float4 oD1 : COLOR1; // Secondary color (front-facing)
float oFog : FOG; // Fog coordinate
float oPts : PSIZE; // Point size
float4 oB0 : TEXCOORD4; // Back-facing primary color
float4 oB1 : TEXCOORD5; // Back-facing secondary color
float4 oT0 : TEXCOORD0; // Texture coordinate set 0
float4 oT1 : TEXCOORD1; // Texture coordinate set 1
float4 oT2 : TEXCOORD2; // Texture coordinate set 2
float4 oT3 : TEXCOORD3; // Texture coordinate set 3
};
float4 reverseScreenspaceTransform(float4 oPos)
{
// Scale screenspace coordinates (0 to viewport width/height) to -1 to +1 range
// On Xbox, oPos should contain the vertex position in screenspace
// We need to reverse this transformation
// Conventionally, each Xbox Vertex Shader includes instructions like this
// mul oPos.xyz, r12, c-38
// +rcc r1.x, r12.w
// mad oPos.xyz, r12, r1.x, c-37
// where c-37 and c-38 are reserved transform values
// Reverse screenspace offset
oPos -= xboxScreenspaceOffset;
// Reverse screenspace scale
oPos /= xboxScreenspaceScale;
// Ensure w is nonzero
if(oPos.w == 0) oPos.w = 1;
// Reverse perspective divide
oPos.xyz *= oPos.w;
return oPos;
}
VS_OUTPUT main(const VS_INPUT xIn)
{
// Input registers
float4 v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15;
// Unpack 16 flags from 4 float4 constant registers
float vRegisterDefaultFlags[16] = (float[16])vRegisterDefaultFlagsPacked;
// Initialize input registers from the vertex buffer data
// Or use the register's default value (which can be changed by the title)
#define init_v(i) v##i = lerp(xIn.v[i], vRegisterDefaultValues[i], vRegisterDefaultFlags[i]);
// Note : unroll manually instead of for-loop, because of the ## concatenation
init_v( 0); init_v( 1); init_v( 2); init_v( 3);
init_v( 4); init_v( 5); init_v( 6); init_v( 7);
init_v( 8); init_v( 9); init_v(10); init_v(11);
init_v(12); init_v(13); init_v(14); init_v(15);
// For passthrough, map output variables to their corresponding input registers
float4 oPos = v0;
float4 oD0 = v3;
float4 oD1 = v4;
float4 oFog = v5;
float4 oPts = v6;
float4 oB0 = v7;
float4 oB1 = v8;
float4 oT0 = v9;
float4 oT1 = v10;
float4 oT2 = v11;
float4 oT3 = v12;
// Copy variables to output struct
VS_OUTPUT xOut;
xOut.oPos = reverseScreenspaceTransform(oPos);
xOut.oD0 = saturate(oD0);
xOut.oD1 = saturate(oD1);
xOut.oFog = oFog.x; // Note : Xbox clamps fog in pixel shader
xOut.oPts = oPts.x;
xOut.oB0 = saturate(oB0);
xOut.oB1 = saturate(oB1);
// Scale textures (TODO: or should we apply this to the input register values?)
xOut.oT0 = oT0 / xboxTextureScale[0];
xOut.oT1 = oT1 / xboxTextureScale[1];
xOut.oT2 = oT2 / xboxTextureScale[2];
xOut.oT3 = oT3 / xboxTextureScale[3];
return xOut;
}

View file

@ -1,3 +1,6 @@
// This starts the raw string (comment to get syntax highlighting, UNCOMMENT to compile) :
R"DELIMITER(// Xbox HLSL vertex shader (template populated at runtime)
struct VS_INPUT
{
float4 v[16] : TEXCOORD;
@ -323,17 +326,40 @@ VS_OUTPUT main(const VS_INPUT xIn)
// Temp variable for paired VS instruction
float4 temp;
// Xbox shader program will be inserted here
// <XBOX SHADER PROGRAM GOES HERE>
// End Xbox shader program
// Xbox shader program)DELIMITER", /* This terminates the header raw string" // */
R"DELIMITER(
// Copy variables to output struct
VS_OUTPUT xOut;
// Fogging
// TODO deduplicate
const float fogDepth = oFog.x; // Don't abs this value! Test-case : DolphinClassic xdk sample
const float fogTableMode = CxbxFogInfo.x;
const float fogDensity = CxbxFogInfo.y;
const float fogStart = CxbxFogInfo.z;
const float fogEnd = CxbxFogInfo.w;
const float FOG_TABLE_NONE = 0;
const float FOG_TABLE_EXP = 1;
const float FOG_TABLE_EXP2 = 2;
const float FOG_TABLE_LINEAR = 3;
float fogFactor;
if(fogTableMode == FOG_TABLE_NONE)
fogFactor = fogDepth;
if(fogTableMode == FOG_TABLE_EXP)
fogFactor = 1 / exp(fogDepth * fogDensity); /* / 1 / e^(d * density)*/
if(fogTableMode == FOG_TABLE_EXP2)
fogFactor = 1 / exp(pow(fogDepth * fogDensity, 2)); /* / 1 / e^((d * density)^2)*/
if(fogTableMode == FOG_TABLE_LINEAR)
fogFactor = (fogEnd - fogDepth) / (fogEnd - fogStart);
xOut.oPos = reverseScreenspaceTransform(oPos);
xOut.oD0 = saturate(oD0);
xOut.oD1 = saturate(oD1);
xOut.oFog = oFog.x; // Note : Xbox clamps fog in pixel shader -> *NEEDS TESTING* /was oFog.x
xOut.oFog = fogFactor; // Note : Xbox clamps fog in pixel shader -> *NEEDS TESTING* /was oFog.x
xOut.oPts = oPts.x;
xOut.oB0 = saturate(oB0);
xOut.oB1 = saturate(oB1);
@ -345,3 +371,5 @@ VS_OUTPUT main(const VS_INPUT xIn)
return xOut;
}
// End of vertex shader footer)DELIMITER" /* This terminates the footer raw string" // */

View file

@ -38,12 +38,10 @@
#include "core\kernel\support\Emu.h"
#include "core\kernel\support\EmuFS.h"
#include "core\kernel\support\NativeHandle.h"
#include "core\kernel\exports\EmuKrnlKe.h"
#include "EmuShared.h"
#include "..\FixedFunctionState.h"
#include "core\hle\D3D8\ResourceTracker.h"
#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For LPDIRECTDRAWSURFACE7
#include "core\hle\D3D8\Direct3D9\Shader.h" // For InitShaderHotloading
#include "core\hle\D3D8\XbVertexBuffer.h"
#include "core\hle\D3D8\XbVertexShader.h"
#include "core\hle\D3D8\XbPixelShader.h" // For DxbxUpdateActivePixelShader
@ -64,7 +62,7 @@
#include "common\input\DInputKeyboardMouse.h"
#include "common\input\InputManager.h"
#include "common/util/strConverter.hpp" // for utf8_to_utf16
#include "VertexShaderCache.h"
#include "VertexShaderSource.h"
#include "Timer.h"
#include <imgui.h>
@ -161,6 +159,8 @@ static IDirect3DQuery *g_pHostQueryWaitForIdle = nullptr;
static IDirect3DQuery *g_pHostQueryCallbackEvent = nullptr;
static int g_RenderUpscaleFactor = 1;
static std::condition_variable g_VBConditionVariable; // Used in BlockUntilVerticalBlank
static std::mutex g_VBConditionMutex; // Used in BlockUntilVerticalBlank
static DWORD g_VBLastSwap = 0;
static xbox::dword_xt g_Xbox_PresentationInterval_Default = D3DPRESENT_INTERVAL_IMMEDIATE;
@ -168,6 +168,7 @@ static xbox::dword_xt g_Xbox_PresentationInterval_Default = D3
static xbox::X_D3DSWAPDATA g_Xbox_SwapData = {0}; // current swap information
static xbox::X_D3DSWAPCALLBACK g_pXbox_SwapCallback = xbox::zeroptr; // Swap/Present callback routine
static xbox::X_D3DVBLANKDATA g_Xbox_VBlankData = {0}; // current vertical blank information
static xbox::X_D3DVBLANKCALLBACK g_pXbox_VerticalBlankCallback = xbox::zeroptr; // Vertical-Blank callback routine
xbox::X_D3DSurface *g_pXbox_BackBufferSurface = xbox::zeroptr;
static xbox::X_D3DSurface *g_pXbox_DefaultDepthStencilSurface = xbox::zeroptr;
@ -227,6 +228,7 @@ static xbox::dword_xt *g_Xbox_D3DDevice; // TODO: This should b
// Static Function(s)
static DWORD WINAPI EmuRenderWindow(LPVOID);
static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
static xbox::void_xt NTAPI EmuUpdateTickCount(xbox::PVOID Arg);
static inline void EmuVerifyResourceIsRegistered(xbox::X_D3DResource *pResource, DWORD D3DUsage, int iTextureStage, DWORD dwSize);
static void UpdateCurrentMSpFAndFPS(); // Used for benchmarking/fps count
static void CxbxImpl_SetRenderTarget(xbox::X_D3DSurface *pRenderTarget, xbox::X_D3DSurface *pNewZStencil);
@ -318,17 +320,15 @@ g_EmuCDPD;
/*XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_LoadVertexShader_4, (xbox::dword_xt) );*/\
XB_MACRO(xbox::hresult_xt, WINAPI, D3DDevice_PersistDisplay, (xbox::void_xt) ); \
XB_MACRO(xbox::hresult_xt, WINAPI, D3DDevice_Reset, (xbox::X_D3DPRESENT_PARAMETERS*) ); \
XB_MACRO(xbox::hresult_xt, WINAPI, D3DDevice_Reset_0__LTCG_edi1, () ); \
XB_MACRO(xbox::hresult_xt, WINAPI, D3DDevice_Reset_0__LTCG_ebx1, () ); \
/*XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SelectVertexShader, (xbox::dword_xt, xbox::dword_xt) );*/\
/*XB_MACRO(xbox::void_xt, __stdcall, D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2, () );*/\
/*XB_MACRO(xbox::void_xt, __stdcall, D3DDevice_SelectVertexShader_4__LTCG_eax1, (xbox::dword_xt) );*/\
/*XB_MACRO(xbox::void_xt, __stdcall, D3DDevice_SelectVertexShader_0, () );*/\
/*XB_MACRO(xbox::void_xt, __stdcall, D3DDevice_SelectVertexShader_4, (xbox::dword_xt) );*/\
/*XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetGammaRamp, (xbox::dword_xt, CONST X_D3DGAMMARAMP*) );*/\
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetIndices, (xbox::X_D3DIndexBuffer*, xbox::uint_xt) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetIndices_4, (xbox::uint_xt) ); \
XB_MACRO(xbox::hresult_xt, WINAPI, D3DDevice_SetLight, (xbox::dword_xt, CONST xbox::X_D3DLIGHT8*) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetPixelShader, (xbox::dword_xt) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetPixelShader_0__LTCG_eax_handle, () ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetPixelShader_0, () ); \
XB_MACRO(xbox::void_xt, __fastcall, D3DDevice_SetRenderState_Simple, (xbox::dword_xt, xbox::dword_xt) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetRenderTarget, (xbox::X_D3DSurface*, xbox::X_D3DSurface*) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetRenderTarget_0, () ); \
@ -339,7 +339,7 @@ g_EmuCDPD;
XB_MACRO(xbox::void_xt, __fastcall, D3DDevice_SetStreamSource_8__LTCG_edx_StreamNumber, (void*, xbox::uint_xt, xbox::X_D3DVertexBuffer*, xbox::uint_xt) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetTexture, (xbox::dword_xt, xbox::X_D3DBaseTexture*) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetTexture_4__LTCG_eax_pTexture, (xbox::dword_xt) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetTexture_4__LTCG_eax_Stage, (xbox::X_D3DBaseTexture*) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetTexture_4, (xbox::X_D3DBaseTexture*) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetPalette, (xbox::dword_xt, xbox::X_D3DPalette*) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetPalette_4, (xbox::X_D3DPalette*) ); \
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetVertexShader, (xbox::dword_xt) ); \
@ -626,13 +626,25 @@ const char *D3DErrorString(HRESULT hResult)
return buffer;
}
void CxbxInitWindow()
void CxbxInitWindow(bool bFullInit)
{
g_EmuShared->GetVideoSettings(&g_XBVideo);
if(g_XBVideo.bFullScreen)
CxbxKrnl_hEmuParent = NULL;
// create timing thread
if (bFullInit && !bLLE_GPU)
{
xbox::HANDLE hThread;
xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, EmuUpdateTickCount, xbox::zeroptr, FALSE);
// We set the priority of this thread a bit higher, to assure reliable timing :
auto nativeHandle = GetNativeHandle(hThread);
assert(nativeHandle);
SetThreadPriority(*nativeHandle, THREAD_PRIORITY_ABOVE_NORMAL);
g_AffinityPolicy->SetAffinityOther(*nativeHandle);
}
/* TODO : Port this Dxbx code :
// create vblank handling thread
{
@ -668,10 +680,6 @@ void CxbxInitWindow()
g_renderbase->SetWindowRelease([] {
ImGui_ImplWin32_Shutdown();
});
(void) g_ShaderSources.Update();
g_ShaderSources.InitShaderHotloading();
}
void DrawUEM(HWND hWnd)
@ -1691,13 +1699,6 @@ static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPar
}
break;
case ID_SYNC_TIME_CHANGE:
{
// Sent by the GUI when it detects WM_TIMECHANGE
xbox::KeSystemTimeChanged.test_and_set();
}
break;
default:
break;
}
@ -1715,14 +1716,6 @@ static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPar
}
break;
case WM_TIMECHANGE:
{
// NOTE: this is only received if the loader was launched from the command line without the GUI
xbox::KeSystemTimeChanged.test_and_set();
return DefWindowProc(hWnd, msg, wParam, lParam);
}
break;
case WM_SYSKEYDOWN:
{
if(wParam == VK_RETURN)
@ -1930,25 +1923,47 @@ std::chrono::steady_clock::time_point GetNextVBlankTime()
return steady_clock::now() + duration_cast<steady_clock::duration>(ms);
}
void hle_vblank()
// timing thread procedure
static xbox::void_xt NTAPI EmuUpdateTickCount(xbox::PVOID Arg)
{
// Note: This whole code block can be removed once NV2A interrupts are implemented
// And Both Swap and Present can be ran unpatched
// Once that is in place, MiniPort + Direct3D will handle this on it's own!
g_Xbox_VBlankData.VBlank++;
CxbxSetThreadName("Cxbx Timing Thread");
// TODO: Fixme. This may not be right...
g_Xbox_SwapData.SwapVBlank = 1;
EmuLog(LOG_LEVEL::DEBUG, "Timing thread is running.");
g_Xbox_VBlankData.Swap = 0;
auto nextVBlankTime = GetNextVBlankTime();
// TODO: This can't be accurate...
g_Xbox_SwapData.TimeUntilSwapVBlank = 0;
while(true)
{
// Wait for VBlank
// Note: This whole code block can be removed once NV2A interrupts are implemented
// And Both Swap and Present can be ran unpatched
// Once that is in place, MiniPort + Direct3D will handle this on it's own!
SleepPrecise(nextVBlankTime);
nextVBlankTime = GetNextVBlankTime();
// TODO: Recalculate this for PAL version if necessary.
// Also, we should check the D3DPRESENT_INTERVAL value for accurracy.
// g_Xbox_SwapData.TimeBetweenSwapVBlanks = 1/60;
g_Xbox_SwapData.TimeBetweenSwapVBlanks = 0;
// Increment the VBlank Counter and Wake all threads there were waiting for the VBlank to occur
std::unique_lock<std::mutex> lk(g_VBConditionMutex);
g_Xbox_VBlankData.VBlank++;
g_VBConditionVariable.notify_all();
// TODO: Fixme. This may not be right...
g_Xbox_SwapData.SwapVBlank = 1;
if(g_pXbox_VerticalBlankCallback != xbox::zeroptr)
{
g_pXbox_VerticalBlankCallback(&g_Xbox_VBlankData);
}
g_Xbox_VBlankData.Swap = 0;
// TODO: This can't be accurate...
g_Xbox_SwapData.TimeUntilSwapVBlank = 0;
// TODO: Recalculate this for PAL version if necessary.
// Also, we should check the D3DPRESENT_INTERVAL value for accurracy.
// g_Xbox_SwapData.TimeBetweenSwapVBlanks = 1/60;
g_Xbox_SwapData.TimeBetweenSwapVBlanks = 0;
}
}
void UpdateDepthStencilFlags(IDirect3DSurface *pDepthStencilSurface)
@ -2256,7 +2271,7 @@ static void CreateDefaultD3D9Device
DrawInitialBlackScreen();
// Set up cache
g_VertexShaderCache.ResetD3DDevice(g_pD3DDevice);
g_VertexShaderSource.ResetD3DDevice(g_pD3DDevice);
// Set up ImGui's render backend
ImGui_ImplDX9_Init(g_pD3DDevice);
@ -2798,13 +2813,10 @@ void Direct3D_CreateDevice_End
{
#if 0 // Unused :
// Set g_Xbox_D3DDevice to point to the Xbox D3D Device
auto it = g_SymbolAddresses.find("D3D_g_pDevice");
auto it = g_SymbolAddresses.find("D3DDEVICE");
if (it != g_SymbolAddresses.end()) {
g_Xbox_D3DDevice = (DWORD*)it->second;
}
else {
EmuLog(LOG_LEVEL::ERROR2, "D3D_g_pDevice was not found!");
}
#endif
UpdateHostBackBufferDesc();
@ -3141,12 +3153,6 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(Direct3D_CreateDevice)
Direct3D_CreateDevice_Start(pPresentationParameters);
// HACK: Disable Software VertexProcessing (Fixes CreateDevice failure in Chihiro titles)
if (BehaviorFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING) {
BehaviorFlags &= ~D3DCREATE_SOFTWARE_VERTEXPROCESSING;
BehaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
}
// Only then call Xbox CreateDevice function
hresult_xt hRet = XB_TRMP(Direct3D_CreateDevice)(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface);
@ -3158,41 +3164,30 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(Direct3D_CreateDevice)
// ******************************************************************
// * patch: D3DDevice_Reset
// ******************************************************************
static void CxbxImpl_Reset(xbox::X_D3DPRESENT_PARAMETERS* pPresentationParameters)
xbox::hresult_xt WINAPI xbox::EMUPATCH(D3DDevice_Reset)
(
X_D3DPRESENT_PARAMETERS* pPresentationParameters
)
{
LOG_FUNC_ONE_ARG(pPresentationParameters)
// Unlike the host version of Reset, The Xbox version does not actually reset the entire device
// Instead, it simply re-creates the backbuffer with a new configuration
// Store the new multisampling configuration
SetXboxMultiSampleType(pPresentationParameters->MultiSampleType);
// Update scaling aspect ratio
SetAspectRatioScale(pPresentationParameters);
// Update scaling aspect ratio
SetAspectRatioScale(pPresentationParameters);
// Since Reset will call create a new backbuffer surface, we can clear our current association
// NOTE: We don't actually free the Xbox data, the Xbox side will do this for us when we call the trampoline below.
// We must not reset the values to nullptr, since the XDK will re-use the same addresses for the data headers
// (they are members of the Direct3DDevice object). if we overwrite then, the reference to the xbox backbuffer will be lost
// and we'll get a black screen.
// and we'll get a black screen.
FreeHostResource(GetHostResourceKey(g_pXbox_BackBufferSurface));
FreeHostResource(GetHostResourceKey(g_pXbox_DefaultDepthStencilSurface));
// Below requirement for patched function(s) in order to function properly.
// Perform xbox's D3DDevice_Reset call.
// Perform CxbxImpl_SetRenderTarget call.
}
xbox::hresult_xt WINAPI xbox::EMUPATCH(D3DDevice_Reset)
(
X_D3DPRESENT_PARAMETERS* pPresentationParameters
)
{
LOG_FUNC_ONE_ARG(pPresentationParameters);
CxbxImpl_Reset(pPresentationParameters);
// Call the Xbox Reset function to do the rest of the work for us
hresult_xt hRet = XB_TRMP(D3DDevice_Reset)(pPresentationParameters);
@ -3203,70 +3198,6 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(D3DDevice_Reset)
return hRet;
}
static void D3DDevice_Reset_0__LTCG_edi1(xbox::X_D3DPRESENT_PARAMETERS* pPresentationParameters)
{
LOG_FUNC_ONE_ARG(pPresentationParameters);
}
__declspec(naked) xbox::hresult_xt WINAPI xbox::EMUPATCH(D3DDevice_Reset_0__LTCG_edi1)()
{
X_D3DPRESENT_PARAMETERS* pPresentationParameters;
__asm {
LTCG_PROLOGUE
mov pPresentationParameters, edi
}
// Log
D3DDevice_Reset_0__LTCG_edi1(pPresentationParameters);
CxbxImpl_Reset(pPresentationParameters);
// Call the Xbox version of DestroyResource
__asm {
mov edi, pPresentationParameters
call XB_TRMP(D3DDevice_Reset_0__LTCG_edi1)
}
CxbxImpl_SetRenderTarget(g_pXbox_RenderTarget, g_pXbox_DepthStencil);
__asm {
LTCG_EPILOGUE
ret
}
}
static void D3DDevice_Reset_0__LTCG_ebx1(xbox::X_D3DPRESENT_PARAMETERS* pPresentationParameters)
{
LOG_FUNC_ONE_ARG(pPresentationParameters);
}
__declspec(naked) xbox::hresult_xt WINAPI xbox::EMUPATCH(D3DDevice_Reset_0__LTCG_ebx1)()
{
X_D3DPRESENT_PARAMETERS* pPresentationParameters;
__asm {
LTCG_PROLOGUE
mov pPresentationParameters, ebx
}
// Log
D3DDevice_Reset_0__LTCG_ebx1(pPresentationParameters);
CxbxImpl_Reset(pPresentationParameters);
// Call the Xbox version of DestroyResource
__asm {
mov ebx, pPresentationParameters
call XB_TRMP(D3DDevice_Reset_0__LTCG_ebx1)
}
CxbxImpl_SetRenderTarget(g_pXbox_RenderTarget, g_pXbox_DepthStencil);
__asm {
LTCG_EPILOGUE
ret
}
}
// ******************************************************************
// * patch: D3DDevice_GetDisplayFieldStatus
@ -3306,12 +3237,12 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_GetDisplayFieldStatus)(X_D3DFIELD_
}
// ******************************************************************
// * patch: D3DDevice_BeginPush_4
// * patch: D3DDevice_BeginPush
// TODO: Find a test case and verify this
// Starting from XDK 4531, this changed to 1 parameter only.
// Is this definition incorrect, or did it change at some point?
// ******************************************************************
xbox::PDWORD WINAPI xbox::EMUPATCH(D3DDevice_BeginPush_4)(dword_xt Count)
xbox::PDWORD WINAPI xbox::EMUPATCH(D3DDevice_BeginPush)(dword_xt Count)
{
LOG_FUNC_ONE_ARG(Count);
@ -3329,11 +3260,11 @@ xbox::PDWORD WINAPI xbox::EMUPATCH(D3DDevice_BeginPush_4)(dword_xt Count)
}
// ******************************************************************
// * patch: D3DDevice_BeginPush_8
// * patch: D3DDevice_BeginPush2
// TODO: Find a test case and verify this: RalliSport Challenge XDK 4134
// For XDK before 4531
// ******************************************************************
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_BeginPush_8)(dword_xt Count, dword_xt** ppPush)
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_BeginPush2)(dword_xt Count, dword_xt** ppPush)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Count)
@ -3656,7 +3587,7 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_LoadVertexShader)
}
// Overload for logging
static void D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2
static void D3DDevice_SelectVertexShader_0
(
xbox::dword_xt Handle,
xbox::dword_xt Address
@ -3671,7 +3602,7 @@ static void D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2
// LTCG specific D3DDevice_SelectVertexShader function...
// This uses a custom calling convention where parameter is passed in EAX, EBX
// Test-case: Star Wars - Battlefront
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2)
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SelectVertexShader_0)
(
)
{
@ -3684,7 +3615,7 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SelectVertexShad
}
// Log
D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2(Handle, Address);
D3DDevice_SelectVertexShader_0(Handle, Address);
CxbxImpl_SelectVertexShader(Handle, Address);
@ -3695,7 +3626,7 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SelectVertexShad
}
// Overload for logging
static void D3DDevice_SelectVertexShader_4__LTCG_eax1
static void D3DDevice_SelectVertexShader_4
(
xbox::dword_xt Handle,
xbox::dword_xt Address
@ -3710,7 +3641,7 @@ static void D3DDevice_SelectVertexShader_4__LTCG_eax1
// LTCG specific D3DDevice_SelectVertexShader function...
// This uses a custom calling convention where parameter is passed in EAX
// Test-case: Aggressive Inline
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SelectVertexShader_4__LTCG_eax1)
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SelectVertexShader_4)
(
dword_xt Address
)
@ -3722,7 +3653,7 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SelectVertexShad
}
// Log
D3DDevice_SelectVertexShader_4__LTCG_eax1(Handle, Address);
D3DDevice_SelectVertexShader_4(Handle, Address);
CxbxImpl_SelectVertexShader(Handle, Address);
@ -4440,7 +4371,7 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTexture_4__LT
}
// Overload for logging
static void D3DDevice_SetTexture_4__LTCG_eax_Stage
static void D3DDevice_SetTexture_4
(
xbox::dword_xt Stage,
xbox::X_D3DBaseTexture *pTexture
@ -4455,7 +4386,7 @@ static void D3DDevice_SetTexture_4__LTCG_eax_Stage
// LTCG specific D3DDevice_SetTexture function...
// This uses a custom calling convention where Stage is passed in EAX
// Test-case: Metal Wolf Chaos
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_Stage)
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTexture_4)
(
X_D3DBaseTexture *pTexture
)
@ -4467,13 +4398,13 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTexture_4__LT
}
// Log
D3DDevice_SetTexture_4__LTCG_eax_Stage(Stage, pTexture);
D3DDevice_SetTexture_4(Stage, pTexture);
// Call the Xbox implementation of this function, to properly handle reference counting for us
__asm {
mov eax, Stage
push pTexture
call XB_TRMP(D3DDevice_SetTexture_4__LTCG_eax_Stage)
call XB_TRMP(D3DDevice_SetTexture_4)
}
g_pXbox_SetTexture[Stage] = pTexture;
@ -6525,6 +6456,31 @@ xbox::bool_xt WINAPI xbox::EMUPATCH(D3DDevice_GetOverlayUpdateStatus)()
return TRUE;
}
// ******************************************************************
// * patch: D3DDevice_BlockUntilVerticalBlank
// ******************************************************************
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_BlockUntilVerticalBlank)()
{
LOG_FUNC();
std::unique_lock<std::mutex> lk(g_VBConditionMutex);
g_VBConditionVariable.wait(lk);
}
// ******************************************************************
// * patch: D3DDevice_SetVerticalBlankCallback
// ******************************************************************
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetVerticalBlankCallback)
(
X_D3DVBLANKCALLBACK pCallback
)
{
LOG_FUNC_ONE_ARG(pCallback);
g_pXbox_VerticalBlankCallback = pCallback;
}
// ******************************************************************
// * patch: D3DDevice_SetRenderState_Simple
// ******************************************************************
@ -7496,11 +7452,11 @@ void CxbxUpdateHostVertexShaderConstants()
}
// Placed this here until we find a better place
const float fogTableMode = XboxRenderStates.GetXboxRenderState(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGTABLEMODE);
const uint32_t fogTableMode = XboxRenderStates.GetXboxRenderState(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGTABLEMODE);
const float fogDensity = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGDENSITY);
const float fogStart = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGSTART);
const float fogEnd = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGEND);
float fogStuff[4] = {fogTableMode, fogDensity, fogStart, fogEnd};
float fogStuff[4] = { (float)fogTableMode, fogDensity, fogStart, fogEnd };
g_pD3DDevice->SetVertexShaderConstantF(CXBX_D3DVS_CONSTREG_FOGINFO, fogStuff, 1);
}
@ -7756,7 +7712,7 @@ xbox::void_xt CxbxImpl_SetPixelShader(xbox::dword_xt Handle)
}
// Overload for logging
static void D3DDevice_SetPixelShader_0__LTCG_eax_handle
static void D3DDevice_SetPixelShader_0
(
xbox::dword_xt Handle
)
@ -7769,7 +7725,7 @@ static void D3DDevice_SetPixelShader_0__LTCG_eax_handle
// Test-case: Metal Wolf Chaos
// Test-case: Lord of the Rings: The Third Age
// Test-case: Midtown Madness 3
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetPixelShader_0__LTCG_eax_handle)()
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetPixelShader_0)()
{
dword_xt Handle;
__asm {
@ -7778,11 +7734,11 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetPixelShader_0
}
// Log
D3DDevice_SetPixelShader_0__LTCG_eax_handle(Handle);
D3DDevice_SetPixelShader_0(Handle);
__asm {
mov eax, Handle
call XB_TRMP(D3DDevice_SetPixelShader_0__LTCG_eax_handle)
call XB_TRMP(D3DDevice_SetPixelShader_0)
}
CxbxImpl_SetPixelShader(Handle);
@ -7815,7 +7771,7 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetPixelShader)
// This uses a custom calling convention where parameter is passed in ECX, EAX and Stack
// Test Case: Conker
// ******************************************************************
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawVertices_4__LTCG_ecx2_eax3)
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawVertices_4)
(
X_D3DPRIMITIVETYPE PrimitiveType
)
@ -7836,30 +7792,6 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawVertices_4__
}
}
// ******************************************************************
// * patch: D3DDevice_DrawVertices_8__LTCG_eax3
// LTCG specific D3DDevice_DrawVertices function...
// ******************************************************************
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawVertices_8__LTCG_eax3)
(
X_D3DPRIMITIVETYPE PrimitiveType,
uint_xt StartVertex
)
{
uint_xt VertexCount;
__asm {
LTCG_PROLOGUE
mov VertexCount, eax
}
EMUPATCH(D3DDevice_DrawVertices)(PrimitiveType, StartVertex, VertexCount);
__asm {
LTCG_EPILOGUE
ret 4
}
}
// ******************************************************************
// * patch: D3DDevice_DrawVertices
// ******************************************************************
@ -7883,7 +7815,7 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawVertices)
return;
}
// TODO : Call unpatched CDevice_SetStateVB[_8](0);
// TODO : Call unpatched CDevice_SetStateVB(0);
CxbxUpdateNativeD3DResources();
@ -8021,7 +7953,7 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawVerticesUP)
// LTCG specific D3DDevice_DrawVerticesUP function...
// This uses a custom calling convention where pVertexStreamZeroData is passed in EBX
// Test-case: NASCAR Heat 20002
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawVerticesUP_12__LTCG_ebx3)
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawVerticesUP_12)
(
X_D3DPRIMITIVETYPE PrimitiveType,
uint_xt VertexCount,
@ -8067,7 +7999,7 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawIndexedVertices)
return;
}
// TODO : Call unpatched CDevice_SetStateVB[_8](g_Xbox_BaseVertexIndex);
// TODO : Call unpatched CDevice_SetStateVB(g_Xbox_BaseVertexIndex);
CxbxUpdateNativeD3DResources();
@ -9076,84 +9008,28 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_GetProjectionViewportMatrix)
// ******************************************************************
// * patch: CDevice_SetStateVB (D3D::CDevice::SetStateVB)
// ******************************************************************
xbox::void_xt WINAPI xbox::EMUPATCH(CDevice_SetStateVB)(ulong_xt Unknown1)
xbox::void_xt WINAPI xbox::EMUPATCH(CDevice_SetStateVB)(ulong_xt Unknown1 )
{
addr_xt _this;
__asm mov _this, ecx;
LOG_FUNC_BEGIN
LOG_FUNC_ARG(_this)
LOG_FUNC_ARG(Unknown1)
LOG_FUNC_END;
LOG_FUNC_ONE_ARG(Unknown1);
// TODO: Anything?
// __asm int 3;
LOG_UNIMPLEMENTED();
}
xbox::void_xt WINAPI xbox::EMUPATCH(CDevice_SetStateVB_8)(addr_xt _this, ulong_xt Unknown1)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(_this)
LOG_FUNC_ARG(Unknown1)
LOG_FUNC_END;
// TODO: Anything?
// __asm int 3;
LOG_UNIMPLEMENTED();
LOG_UNIMPLEMENTED();
}
// ******************************************************************
// * patch: CDevice_SetStateUP (D3D::CDevice::SetStateUP)
// ******************************************************************
xbox::void_xt CxbxrImpl_CDevice_SetStateUP(xbox::addr_xt _this)
xbox::void_xt WINAPI xbox::EMUPATCH(CDevice_SetStateUP)()
{
LOG_FUNC();
LOG_UNIMPLEMENTED();
// TODO: Anything?
//__asm int 3;
}
xbox::void_xt WINAPI xbox::EMUPATCH(CDevice_SetStateUP)()
{
addr_xt _this;
__asm mov _this, ecx;
LOG_FUNC_ONE_ARG(_this);
CxbxrImpl_CDevice_SetStateUP(_this);
}
xbox::void_xt WINAPI xbox::EMUPATCH(CDevice_SetStateUP_4)(xbox::addr_xt _this)
{
LOG_FUNC_ONE_ARG(_this);
CxbxrImpl_CDevice_SetStateUP(_this);
}
static void CDevice_SetStateUP_0__LTCG_esi1(xbox::addr_xt _this)
{
LOG_FUNC_ONE_ARG(_this);
}
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(CDevice_SetStateUP_0__LTCG_esi1)()
{
addr_xt _this;
__asm {
LTCG_PROLOGUE
mov _this, esi
}
// Log
CDevice_SetStateUP_0__LTCG_esi1(_this);
CxbxrImpl_CDevice_SetStateUP(_this);
__asm {
LTCG_EPILOGUE
ret
}
// __asm int 3;
}
// ******************************************************************

View file

@ -4256,27 +4256,3 @@ HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShaderInput)
return 0;
}
// ******************************************************************
// * patch: D3DDevice_BlockUntilVerticalBlank
// ******************************************************************
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_BlockUntilVerticalBlank)()
{
LOG_FUNC();
std::unique_lock<std::mutex> lk(g_VBConditionMutex);
g_VBConditionVariable.wait(lk);
}
// ******************************************************************
// * patch: D3DDevice_SetVerticalBlankCallback
// ******************************************************************
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetVerticalBlankCallback)
(
X_D3DVBLANKCALLBACK pCallback
)
{
LOG_FUNC_ONE_ARG(pCallback);
g_pXbox_VerticalBlankCallback = pCallback;
}

View file

@ -40,7 +40,7 @@
void LookupTrampolinesD3D();
// initialize render window
extern void CxbxInitWindow();
extern void CxbxInitWindow(bool bFullInit);
void CxbxUpdateNativeD3DResources();
@ -144,14 +144,14 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_GetDisplayFieldStatus)
);
// ******************************************************************
// * patch: D3DDevice_BeginPush_4
// * patch: D3DDevice_BeginPush
// ******************************************************************
xbox::PDWORD WINAPI EMUPATCH(D3DDevice_BeginPush_4)(dword_xt Count);
xbox::PDWORD WINAPI EMUPATCH(D3DDevice_BeginPush)(dword_xt Count);
// ******************************************************************
// * patch: D3DDevice_BeginPush_8
// * patch: D3DDevice_BeginPush2 //two arg version for xdk before 4531
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_BeginPush_8)(dword_xt Count, dword_xt **ppPush);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_BeginPush2)(dword_xt Count, dword_xt **ppPush);
// ******************************************************************
// * patch: D3DDevice_EndPush
@ -213,8 +213,8 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader)
dword_xt Address
);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_4__LTCG_eax1)
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_0)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_4)
(
dword_xt Address
);
@ -359,10 +359,6 @@ xbox::hresult_xt WINAPI EMUPATCH(D3DDevice_Reset)
X_D3DPRESENT_PARAMETERS *pPresentationParameters
);
xbox::hresult_xt WINAPI EMUPATCH(D3DDevice_Reset_0__LTCG_edi1)();
xbox::hresult_xt WINAPI EMUPATCH(D3DDevice_Reset_0__LTCG_ebx1)();
// ******************************************************************
// * patch: D3DDevice_GetRenderTarget
// ******************************************************************
@ -509,7 +505,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader)
dword_xt Handle
);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader_0__LTCG_eax_handle)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader_0)();
// ******************************************************************
// * patch: D3DDevice_CreateTexture2
@ -615,7 +611,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_pTexture)
dword_xt Stage
);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_Stage)
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4)
(
X_D3DBaseTexture *pTexture
);
@ -1394,27 +1390,18 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetVertexShader_0)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices)
(
X_D3DPRIMITIVETYPE PrimitiveType,
uint_xt StartVertex,
uint_xt VertexCount
uint_xt StartVertex,
uint_xt VertexCount
);
// ******************************************************************
// * patch: D3DDevice_DrawVertices_4__LTCG_ecx2_eax3
// * patch: D3DDevice_DrawVertices_4
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices_4__LTCG_ecx2_eax3)
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices_4)
(
X_D3DPRIMITIVETYPE PrimitiveType
);
// ******************************************************************
// * patch: D3DDevice_DrawVertices_8__LTCG_eax3
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices_8__LTCG_eax3)
(
X_D3DPRIMITIVETYPE PrimitiveType,
uint_xt StartVertex
);
// ******************************************************************
// * patch: D3DDevice_DrawVerticesUP
// ******************************************************************
@ -1426,11 +1413,11 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVerticesUP)
uint_xt VertexStreamZeroStride
);
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVerticesUP_12__LTCG_ebx3)
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVerticesUP_12)
(
X_D3DPRIMITIVETYPE PrimitiveType,
uint_xt VertexCount,
uint_xt VertexStreamZeroStride
uint_xt VertexCount,
uint_xt VertexStreamZeroStride
);
@ -1910,15 +1897,12 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_GetTexture)
// ******************************************************************
// * patch: CDevice_SetStateVB (D3D::CDevice::SetStateVB)
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateVB)(xbox::ulong_xt Unknown1);
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateVB_8)(xbox::addr_xt _this, xbox::ulong_xt Unknown1);
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateVB)( xbox::ulong_xt Unknown1 );
// ******************************************************************
// * patch: CDevice_SetStateUP (D3D::CDevice::SetStateUP)
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateUP)();
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateUP_4)(xbox::addr_xt _this);
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateUP_0__LTCG_esi1)();
// ******************************************************************
// * patch: D3DDevice_SetStipple

View file

@ -237,24 +237,6 @@ TextureArgs ExecuteTextureStage(
float4 main(const PS_INPUT input) : COLOR {
// Calculate the fog factor
float fogFactor;
if (state.FogEnable == 0){
fogFactor = 1;
}
else{
if (state.FogTableMode == FOG_TABLE_NONE)
fogFactor = input.iFog;
if (state.FogTableMode == FOG_TABLE_EXP)
fogFactor = 1 / exp(input.iFog * state.FogDensity); // 1 / e^(d * density)
if (state.FogTableMode == FOG_TABLE_EXP2)
fogFactor = 1 / exp(pow(input.iFog * state.FogDensity, 2)); // 1 / e^((d * density)^2)
if (state.FogTableMode == FOG_TABLE_LINEAR)
fogFactor = (state.FogEnd - input.iFog) / (state.FogEnd - state.FogStart); // (end - d) / (end - start)
if (state.FogEnable == 0)
fogFactor = 1;
}
TexCoords = input.iT;
// Each stage is passed and returns
@ -302,7 +284,7 @@ float4 main(const PS_INPUT input) : COLOR {
// Add fog if enabled
if (state.FogEnable) {
ctx.CURRENT.rgb = lerp(state.FogColor.rgb, ctx.CURRENT.rgb, saturate(fogFactor));
ctx.CURRENT.rgb = lerp(state.FogColor.rgb, ctx.CURRENT.rgb, saturate(input.iFog));
}
// Add specular if enabled

View file

@ -63,16 +63,10 @@ namespace FixedFunctionPixelShader {
const float X_D3DTA_COMPLEMENT = 0x00000010; // take 1.0 - x (read modifier)
const float X_D3DTA_ALPHAREPLICATE = 0x00000020; // replicate alpha to color components (read modifier)
const int SAMPLE_NONE = 0;
const int SAMPLE_2D = 1;
const int SAMPLE_3D = 2;
const int SAMPLE_CUBE = 3;
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/fog-formulas
const float FOG_TABLE_NONE = 0;
const float FOG_TABLE_EXP = 1;
const float FOG_TABLE_EXP2 = 2;
const float FOG_TABLE_LINEAR = 3;
const int SAMPLE_NONE = 0;
const int SAMPLE_2D = 1;
const int SAMPLE_3D = 2;
const int SAMPLE_CUBE = 3;
// This state is passed to the shader
struct PsTextureStageState {
@ -131,11 +125,7 @@ namespace FixedFunctionPixelShader {
alignas(16) float SpecularEnable;
alignas(16) float FogEnable;
alignas(16) float3 FogColor;
alignas(16) float FogTableMode;
alignas(16) float FogDensity;
alignas(16) float FogStart;
alignas(16) float FogEnd;
};
};
#ifdef __cplusplus
} // FixedFunctionPixelShader namespace
#endif

View file

@ -288,8 +288,20 @@ float DoFog(const VS_INPUT xIn)
fogDepth = abs(Projection.Position.z);
if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_W)
fogDepth = Projection.Position.w;
return fogDepth;
// Calculate the fog factor
// Some of this might be better done in the pixel shader?
float fogFactor;
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_NONE)
fogFactor = fogDepth;
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_EXP)
fogFactor = 1 / exp(fogDepth * state.Fog.Density); // 1 / e^(d * density)
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_EXP2)
fogFactor = 1 / exp(pow(fogDepth * state.Fog.Density, 2)); // 1 / e^((d * density)^2)
if (state.Fog.TableMode == FixedFunctionVertexShader::FOG_TABLE_LINEAR)
fogFactor = (state.Fog.End - fogDepth) / (state.Fog.End - state.Fog.Start); // (end - d) / (end - start)
return fogFactor;
}
float4 DoTexCoord(const uint stage, const VS_INPUT xIn)
@ -339,9 +351,9 @@ float4 DoTexCoord(const uint stage, const VS_INPUT xIn)
// TODO move alongside the texture transformation when it stops angering the HLSL compiler
const float componentCount = state.TexCoordComponentCount[texCoordIndex];
if (componentCount == 1)
texCoord.yzw = float3(0, 0, 1);
texCoord.yzw = float3(1, 0, 0);
if (componentCount == 2)
texCoord.zw = float2(0, 1);
texCoord.zw = float2(1, 0);
if (componentCount == 3)
texCoord.w = 1;
} // Generate texture coordinates
@ -378,15 +390,14 @@ float4 DoTexCoord(const uint stage, const VS_INPUT xIn)
// Test case: ProjectedTexture sample, which uses 3 coordinates
// We'll need to implement the divide when D3D stops handling it for us?
// https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dtexturetransformflags
// padding makes no differences in LLE. LLE works with ProjectedTexture sample without padding, but to be devided by w is necessary.
if (projected)
{
//if (countFlag == 1)
//texCoord.yz = texCoord.x;
//if (countFlag == 2)
//texCoord.z = texCoord.y;
//texCoord.xyzw = texCoord.xyzw / texCoord.w;
if (countFlag == 1)
texCoord.yzw = texCoord.x;
if (countFlag == 2)
texCoord.zw = texCoord.y;
if (countFlag == 3)
texCoord.w = texCoord.z;
}
return texCoord;

View file

@ -289,7 +289,12 @@ bool IsTextureSampled(DecodedRegisterCombiner* pShader, int reg)
void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
{
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[0]; // Start with the HLSL template header
// Include HLSL header and footer as raw strings :
static const std::string hlsl_template[4] = {
#include "core\hle\D3D8\Direct3D9\CxbxPixelShaderTemplate.hlsl"
};
hlsl << hlsl_template[0]; // Start with the HLSL template header
hlsl << "\n#define ALPHAKILL {"
<< (pShader->AlphaKill[0] ? "true, " : "false, ")
@ -336,9 +341,9 @@ void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
OutputDefineFlag(hlsl, pShader->FinalCombiner.ComplementV1, "PS_FINALCOMBINERSETTING_COMPLEMENT_V1");
OutputDefineFlag(hlsl, pShader->FinalCombiner.ComplementR0, "PS_FINALCOMBINERSETTING_COMPLEMENT_R0");
OutputDefineFlag(hlsl, pShader->FinalCombiner.ClampSum, "PS_FINALCOMBINERSETTING_CLAMP_SUM");
hlsl << '\n';
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[1];
hlsl << hlsl_template[1];
hlsl << hlsl_template[2];
// Generate all four texture stages
for (unsigned i = 0; i < PSH_XBOX_MAX_T_REGISTER_COUNT; i++) {
@ -385,7 +390,7 @@ void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
FinalCombinerStageHlsl(hlsl, pShader->FinalCombiner, pShader->hasFinalCombiner);
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[2]; // Finish with the HLSL template footer
hlsl << hlsl_template[3]; // Finish with the HLSL template footer
}
// recompile xbox pixel shader function

View file

@ -36,14 +36,20 @@ void SetXboxMultiSampleType(xbox::X_D3DMULTISAMPLE_TYPE value);
bool XboxRenderStateConverter::Init()
{
// Get render state
if (g_SymbolAddresses.find("D3D_g_RenderState") != g_SymbolAddresses.end()) {
D3D__RenderState = (uint32_t*)g_SymbolAddresses["D3D_g_RenderState"];
if (g_SymbolAddresses.find("D3DDeferredRenderState") != g_SymbolAddresses.end()) {
D3D__RenderState = (uint32_t*)g_SymbolAddresses["D3DDeferredRenderState"];
} else {
EmuLog(LOG_LEVEL::ERROR2, "D3D_g_RenderState was not found!");
return false;
}
// At this point, D3D__RenderState points to the first Deferred render state
// Do a little magic to verify that it's correct, then count back to determine the
// start offset of the entire structure
VerifyAndFixDeferredRenderStateOffset();
// Now use the verified Deferred offset to derive the D3D__RenderState offset
DeriveRenderStateOffsetFromDeferredRenderStateOffset();
// Build a mapping of Cxbx Render State indexes to indexes within the current XDK
BuildRenderStateMappingTable();
@ -64,6 +70,54 @@ bool IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo& aRenderStateInf
return bIsRenderStateAvailable;
}
void XboxRenderStateConverter::VerifyAndFixDeferredRenderStateOffset()
{
DWORD CullModeOffset = g_SymbolAddresses["D3DRS_CULLMODE"];
// If we found a valid CullMode offset, verify the symbol location
if (CullModeOffset == 0) {
EmuLog(LOG_LEVEL::WARNING, "D3DRS_CULLMODE could not be found. Please update the XbSymbolDatabase submodule");
return;
}
// Calculate index of D3DRS_CULLMODE for this XDK. We start counting from the first deferred state (D3DRS_FOGENABLE)
DWORD CullModeIndex = 0;
for (int i = xbox::X_D3DRS_DEFERRED_FIRST; i < xbox::X_D3DRS_CULLMODE; i++) {
auto RenderStateInfo = GetDxbxRenderStateInfo(i);
if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) {
CullModeIndex++;
}
}
// If the offset was incorrect, calculate the correct offset, log it, and fix it
if ((DWORD)(&D3D__RenderState[CullModeIndex]) != CullModeOffset) {
DWORD CorrectOffset = CullModeOffset - (CullModeIndex * sizeof(DWORD));
EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredRenderState returned by XboxSymbolDatabase (0x%08X) was incorrect. Correcting to be 0x%08X.\nPlease file an issue with the XbSymbolDatabase project", D3D__RenderState, CorrectOffset);
D3D__RenderState = (uint32_t*)CorrectOffset;
}
}
void XboxRenderStateConverter::DeriveRenderStateOffsetFromDeferredRenderStateOffset()
{
// When this function is called. D3D__RenderState actually points to the first deferred render state
// (this is X_D3DRS_FOGENABLE). We can count back from this using our RenderStateInfo table to find
// the start of D3D__RenderStates.
// Count the number of render states (for this XDK) between 0 and the first deferred render state (D3DRS_FOGENABLE)
int FirstDeferredRenderStateOffset = 0;
for (unsigned int RenderState = xbox::X_D3DRS_FIRST; RenderState < xbox::X_D3DRS_DEFERRED_FIRST; RenderState++) {
// if the current renderstate exists in this XDK version, count it
auto RenderStateInfo = GetDxbxRenderStateInfo(RenderState);
if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) {
FirstDeferredRenderStateOffset++;
}
}
// At this point, FirstDeferredRenderStateOffset should point to the index of D3DRS_FOGENABLE for the given XDK
// This will be correct as long as our table DxbxRenderStateInfo is correct
// We can get the correct 0 offset by using a negative index
D3D__RenderState = &D3D__RenderState[-FirstDeferredRenderStateOffset];
}
void XboxRenderStateConverter::BuildRenderStateMappingTable()
{
EmuLog(LOG_LEVEL::INFO, "Building Cxbx to XDK Render State Mapping Table");

View file

@ -29,18 +29,10 @@
#include <d3dcompiler.h>
#include "Shader.h"
#include "common/FilePaths.hpp" // For szFilePath_CxbxReloaded_Exe
#include "core\kernel\init\CxbxKrnl.h" // LOG_TEST_CASE
#include "core\kernel\support\Emu.h" // EmuLog
#include <filesystem>
#include <fstream>
#include <array>
#include <thread>
//#include <sstream>
ShaderSources g_ShaderSources;
std::string DebugPrependLineNumbers(std::string shaderString) {
std::stringstream shader(shaderString);
auto debugShader = std::stringstream();
@ -148,173 +140,3 @@ extern HRESULT EmuCompileShader
return hRet;
}
std::ifstream OpenWithRetry(const std::string& path) {
auto fstream = std::ifstream(path);
int failures = 0;
while (fstream.fail()) {
Sleep(50);
fstream = std::ifstream(path);
if (failures++ > 10) {
// crash?
CxbxrAbort("Error opening shader file: %s", path);
break;
}
}
return fstream;
}
int ShaderSources::Update() {
int versionOnDisk = shaderVersionOnDisk;
if (shaderVersionLoadedFromDisk != versionOnDisk) {
LoadShadersFromDisk();
shaderVersionLoadedFromDisk = versionOnDisk;
}
return shaderVersionLoadedFromDisk;
}
void ShaderSources::LoadShadersFromDisk() {
const auto hlslDir = std::filesystem::path(szFilePath_CxbxReloaded_Exe)
.parent_path()
.append("hlsl");
// Pixel Shader Template
{
std::stringstream tmp;
auto dir = hlslDir;
dir.append("CxbxPixelShaderTemplate.hlsl");
tmp << OpenWithRetry(dir.string()).rdbuf();
std::string hlsl = tmp.str();
// Split the HLSL file on insertion points
std::array<std::string, 2> insertionPoints = {
"// <HARDCODED STATE GOES HERE>\n",
"// <XBOX SHADER PROGRAM GOES HERE>\n",
};
int pos = 0;
for (int i = 0; i < insertionPoints.size(); i++) {
auto insertionPoint = insertionPoints[i];
auto index = hlsl.find(insertionPoint, pos);
if (index == std::string::npos) {
// Handle broken shaders
this->pixelShaderTemplateHlsl[i] = "";
}
else {
this->pixelShaderTemplateHlsl[i] = hlsl.substr(pos, index - pos);
pos = index + insertionPoint.length();
}
}
this->pixelShaderTemplateHlsl[insertionPoints.size()] = hlsl.substr(pos);
}
// Fixed Function Pixel Shader
{
auto dir = hlslDir;
this->fixedFunctionPixelShaderPath = dir.append("FixedFunctionPixelShader.hlsl").string();
std::stringstream tmp;
tmp << OpenWithRetry(this->fixedFunctionPixelShaderPath).rdbuf();
this->fixedFunctionPixelShaderHlsl = tmp.str();
}
// Vertex Shader Template
{
std::stringstream tmp;
auto dir = hlslDir;
dir.append("CxbxVertexShaderTemplate.hlsl");
tmp << OpenWithRetry(dir.string()).rdbuf();
std::string hlsl = tmp.str();
const std::string insertionPoint = "// <XBOX SHADER PROGRAM GOES HERE>\n";
auto index = hlsl.find(insertionPoint);
if (index == std::string::npos) {
// Handle broken shaders
this->vertexShaderTemplateHlsl[0] = hlsl;
this->vertexShaderTemplateHlsl[1] = "";
}
else
{
this->vertexShaderTemplateHlsl[0] = hlsl.substr(0, index);
this->vertexShaderTemplateHlsl[1] = hlsl.substr(index + insertionPoint.length());
}
}
// Fixed Function Vertex Shader
{
auto dir = hlslDir;
this->fixedFunctionVertexShaderPath = dir.append("FixedFunctionVertexShader.hlsl").string();
std::stringstream tmp;
tmp << OpenWithRetry(this->fixedFunctionVertexShaderPath).rdbuf();
this->fixedFunctionVertexShaderHlsl = tmp.str();
}
// Passthrough Vertex Shader
{
auto dir = hlslDir;
this->vertexShaderPassthroughPath = dir.append("CxbxVertexShaderPassthrough.hlsl").string();
std::stringstream tmp;
tmp << OpenWithRetry(this->vertexShaderPassthroughPath).rdbuf();
this->vertexShaderPassthroughHlsl = tmp.str();
}
}
void ShaderSources::InitShaderHotloading() {
static std::jthread fsWatcherThread;
if (fsWatcherThread.joinable()) {
EmuLog(LOG_LEVEL::ERROR2, "Ignoring request to start shader file watcher - it has already been started.");
return;
}
EmuLog(LOG_LEVEL::DEBUG, "Starting shader file watcher...");
fsWatcherThread = std::jthread([]{
// Determine the filename and directory for the fixed function shader
char cxbxExePath[MAX_PATH];
GetModuleFileName(GetModuleHandle(nullptr), cxbxExePath, MAX_PATH);
auto hlslDir = std::filesystem::path(cxbxExePath).parent_path().append("hlsl/");
HANDLE changeHandle = FindFirstChangeNotification(hlslDir.string().c_str(), false, FILE_NOTIFY_CHANGE_LAST_WRITE);
if (changeHandle == INVALID_HANDLE_VALUE) {
DWORD errorCode = GetLastError();
EmuLog(LOG_LEVEL::ERROR2, "Error initializing shader file watcher: %d", errorCode);
return 1;
}
while (true) {
if (FindNextChangeNotification(changeHandle)) {
WaitForSingleObject(changeHandle, INFINITE);
// Wait for changes to stop..
// Will usually be at least two - one for the file and one for the directory
while (true) {
FindNextChangeNotification(changeHandle);
if (WaitForSingleObject(changeHandle, 100) == WAIT_TIMEOUT) {
break;
}
}
EmuLog(LOG_LEVEL::DEBUG, "Change detected in shader folder");
g_ShaderSources.shaderVersionOnDisk++;
}
else {
EmuLog(LOG_LEVEL::ERROR2, "Shader filewatcher failed to get the next notification");
break;
}
}
EmuLog(LOG_LEVEL::DEBUG, "Shader file watcher exiting...");
// until there is a way to disable hotloading
// this is always an error
FindCloseChangeNotification(changeHandle);
return 1;
});
}

View file

@ -1,6 +1,5 @@
#pragma once
#include <atomic>
#include <string> // std::string
#include <d3dcompiler.h> // ID3DBlob (via d3d9.h > d3d11shader.h > d3dcommon.h)
@ -11,38 +10,3 @@ extern HRESULT EmuCompileShader
ID3DBlob** ppHostShader,
const char* pSourceName = nullptr
);
struct ShaderSources {
// Pixel Shader
std::string pixelShaderTemplateHlsl[3];
std::string fixedFunctionPixelShaderHlsl;
std::string fixedFunctionPixelShaderPath;
// Vertex Shader
std::string vertexShaderTemplateHlsl[2];
std::string fixedFunctionVertexShaderHlsl;
std::string fixedFunctionVertexShaderPath;
std::string vertexShaderPassthroughHlsl;
std::string vertexShaderPassthroughPath;
// Load shaders from disk (if out-of-date)
// and return the current loaded shader version
int Update();
// Start a thread to watch for changes in the shader folder
void InitShaderHotloading();
private:
void LoadShadersFromDisk();
// counts upwards on every change detected to the shader source files at runtime
std::atomic_int shaderVersionOnDisk = 0;
// current loaded shader version
// Initialized to < shaderVersionOnDisk
int shaderVersionLoadedFromDisk = -1;
};
extern ShaderSources g_ShaderSources;

View file

@ -79,12 +79,11 @@ TextureStateInfo CxbxTextureStateInfo[] = {
bool XboxTextureStateConverter::Init(XboxRenderStateConverter* pState)
{
// Deferred states start at 0, this means that D3D_g_DeferredTextureState IS D3D__TextureState
// Deferred states start at 0, this means that D3DDeferredTextureState IS D3D__TextureState
// No further works is required to derive the offset
if (g_SymbolAddresses.find("D3D_g_DeferredTextureState") != g_SymbolAddresses.end()) {
D3D__TextureState = (uint32_t*)g_SymbolAddresses["D3D_g_DeferredTextureState"];
if (g_SymbolAddresses.find("D3DDeferredTextureState") != g_SymbolAddresses.end()) {
D3D__TextureState = (uint32_t*)g_SymbolAddresses["D3DDeferredTextureState"];
} else {
EmuLog(LOG_LEVEL::ERROR2, "D3D_g_DeferredTextureState was not found!");
return false;
}

View file

@ -4,7 +4,9 @@
#include "VertexShader.h" // EmuCompileVertexShader
#include "core\kernel\init\CxbxKrnl.h" // implicit CxbxKrnl_Xbe used in LOG_TEST_CASE
#include "core\kernel\support\Emu.h" // LOG_TEST_CASE (via Logging.h)
#include "common/FilePaths.hpp" // For szFilePath_CxbxReloaded_Exe
#include <fstream>
#include <sstream> // std::stringstream
extern const char* g_vs_model = vs_model_3_0;
@ -288,23 +290,26 @@ extern HRESULT EmuCompileVertexShader
ID3DBlob** ppHostShader
)
{
assert(pIntermediateShader->Instructions.size() > 0);
// Include HLSL header and footer as raw strings :
static std::string hlsl_template[2] = {
#include "core\hle\D3D8\Direct3D9\CxbxVertexShaderTemplate.hlsl"
};
// Combine the shader template with the shader program
auto hlsl_stream = std::stringstream();
hlsl_stream << g_ShaderSources.vertexShaderTemplateHlsl[0]; // Start with the HLSL template header
hlsl_stream << hlsl_template[0]; // Start with the HLSL template header
assert(pIntermediateShader->Instructions.size() > 0);
BuildShader(pIntermediateShader, hlsl_stream);
hlsl_stream << g_ShaderSources.vertexShaderTemplateHlsl[1]; // Finish with the HLSL template footer
hlsl_stream << hlsl_template[1]; // Finish with the HLSL template footer
std::string hlsl_str = hlsl_stream.str();
const char* notionalSourceName = "CxbxVertexShaderTemplate.hlsl";
HRESULT hRet = EmuCompileShader(hlsl_str, g_vs_model, ppHostShader, notionalSourceName);
HRESULT hRet = EmuCompileShader(hlsl_str, g_vs_model, ppHostShader, "CxbxVertexShaderTemplate.hlsl");
if (FAILED(hRet) && (g_vs_model != vs_model_3_0)) {
// If the shader failed in the default vertex shader model, retry in vs_model_3_0
// This allows shaders too large for 2_a to be compiled (Test Case: Shenmue 2)
EmuLog(LOG_LEVEL::WARNING, "Shader compile failed. Retrying with shader model 3.0");
hRet = EmuCompileShader(hlsl_str, vs_model_3_0, ppHostShader, notionalSourceName);
hRet = EmuCompileShader(hlsl_str, vs_model_3_0, ppHostShader, "CxbxVertexShaderTemplate.hlsl");
}
return hRet;
@ -312,10 +317,174 @@ extern HRESULT EmuCompileVertexShader
extern void EmuCompileFixedFunction(ID3DBlob** ppHostShader)
{
EmuCompileShader(g_ShaderSources.fixedFunctionVertexShaderHlsl, g_vs_model, ppHostShader, g_ShaderSources.fixedFunctionVertexShaderPath.c_str());
static ID3DBlob* pShader = nullptr;
// TODO does this need to be thread safe?
if (pShader == nullptr) {
// Determine the filename and directory for the fixed function shader
auto hlslDir = std::filesystem::path(szFilePath_CxbxReloaded_Exe)
.parent_path()
.append("hlsl");
auto sourceFile = hlslDir.append("FixedFunctionVertexShader.hlsl").string();
// Load the shader into a string
std::ifstream hlslStream(sourceFile);
std::stringstream hlsl;
hlsl << hlslStream.rdbuf();
// Compile the shader
EmuCompileShader(hlsl.str(), g_vs_model, &pShader, sourceFile.c_str());
}
*ppHostShader = pShader;
};
extern void EmuCompileXboxPassthrough(ID3DBlob** ppHostShader)
static ID3DBlob* pPassthroughShader = nullptr;
extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader)
{
EmuCompileShader(g_ShaderSources.vertexShaderPassthroughHlsl, g_vs_model, ppHostShader, g_ShaderSources.vertexShaderPassthroughPath.c_str());
// TODO does this need to be thread safe?
if (pPassthroughShader == nullptr) {
auto hlsl =
R"(
// Xbox HLSL pretransformed vertex shader
// Default values for vertex registers, and whether to use them
uniform float4 vRegisterDefaultValues[16] : register(c192);
uniform float4 vRegisterDefaultFlagsPacked[4] : register(c208);
uniform float4 xboxScreenspaceScale : register(c212);
uniform float4 xboxScreenspaceOffset : register(c213);
uniform float4 xboxTextureScale[4] : register(c214);
// Parameters for mapping the shader's fog output value to a fog factor
uniform float4 CxbxFogInfo: register(c218); // = CXBX_D3DVS_CONSTREG_FOGINFO
struct VS_INPUT
{
float4 v[16] : TEXCOORD;
};
// Output registers
struct VS_OUTPUT
{
float4 oPos : POSITION; // Homogeneous clip space position
float4 oD0 : COLOR0; // Primary color (front-facing)
float4 oD1 : COLOR1; // Secondary color (front-facing)
float oFog : FOG; // Fog coordinate
float oPts : PSIZE; // Point size
float4 oB0 : TEXCOORD4; // Back-facing primary color
float4 oB1 : TEXCOORD5; // Back-facing secondary color
float4 oT0 : TEXCOORD0; // Texture coordinate set 0
float4 oT1 : TEXCOORD1; // Texture coordinate set 1
float4 oT2 : TEXCOORD2; // Texture coordinate set 2
float4 oT3 : TEXCOORD3; // Texture coordinate set 3
};
float4 reverseScreenspaceTransform(float4 oPos)
{
// Scale screenspace coordinates (0 to viewport width/height) to -1 to +1 range
// On Xbox, oPos should contain the vertex position in screenspace
// We need to reverse this transformation
// Conventionally, each Xbox Vertex Shader includes instructions like this
// mul oPos.xyz, r12, c-38
// +rcc r1.x, r12.w
// mad oPos.xyz, r12, r1.x, c-37
// where c-37 and c-38 are reserved transform values
// Reverse screenspace offset
oPos -= xboxScreenspaceOffset;
// Reverse screenspace scale
oPos /= xboxScreenspaceScale;
// Ensure w is nonzero
if(oPos.w == 0) oPos.w = 1;
// Reverse perspective divide
oPos.xyz *= oPos.w;
return oPos;
}
VS_OUTPUT main(const VS_INPUT xIn)
{
// Input registers
float4 v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15;
// Unpack 16 flags from 4 float4 constant registers
float vRegisterDefaultFlags[16] = (float[16])vRegisterDefaultFlagsPacked;
// Initialize input registers from the vertex buffer data
// Or use the register's default value (which can be changed by the title)
#define init_v(i) v##i = lerp(xIn.v[i], vRegisterDefaultValues[i], vRegisterDefaultFlags[i]);
// Note : unroll manually instead of for-loop, because of the ## concatenation
init_v( 0); init_v( 1); init_v( 2); init_v( 3);
init_v( 4); init_v( 5); init_v( 6); init_v( 7);
init_v( 8); init_v( 9); init_v(10); init_v(11);
init_v(12); init_v(13); init_v(14); init_v(15);
// For passthrough, map output variables to their corresponding input registers
float4 oPos = v0;
float4 oD0 = v3;
float4 oD1 = v4;
float4 oFog = v5;
float4 oPts = v6;
float4 oB0 = v7;
float4 oB1 = v8;
float4 oT0 = v9;
float4 oT1 = v10;
float4 oT2 = v11;
float4 oT3 = v12;
// Copy variables to output struct
VS_OUTPUT xOut;
// Fogging
// TODO deduplicate
const float fogDepth = abs(oFog.x);
const float fogTableMode = CxbxFogInfo.x;
const float fogDensity = CxbxFogInfo.y;
const float fogStart = CxbxFogInfo.z;
const float fogEnd = CxbxFogInfo.w;
const float FOG_TABLE_NONE = 0;
const float FOG_TABLE_EXP = 1;
const float FOG_TABLE_EXP2 = 2;
const float FOG_TABLE_LINEAR = 3;
float fogFactor;
if(fogTableMode == FOG_TABLE_NONE)
fogFactor = fogDepth;
if(fogTableMode == FOG_TABLE_EXP)
fogFactor = 1 / exp(fogDepth * fogDensity); /* / 1 / e^(d * density)*/
if(fogTableMode == FOG_TABLE_EXP2)
fogFactor = 1 / exp(pow(fogDepth * fogDensity, 2)); /* / 1 / e^((d * density)^2)*/
if(fogTableMode == FOG_TABLE_LINEAR)
fogFactor = (fogEnd - fogDepth) / (fogEnd - fogStart);
xOut.oPos = reverseScreenspaceTransform(oPos);
xOut.oD0 = saturate(oD0);
xOut.oD1 = saturate(oD1);
xOut.oFog = fogFactor; // Note : Xbox clamps fog in pixel shader
xOut.oPts = oPts.x;
xOut.oB0 = saturate(oB0);
xOut.oB1 = saturate(oB1);
// Scale textures (TODO : or should we apply this to the input register values?)
xOut.oT0 = oT0 / xboxTextureScale[0];
xOut.oT1 = oT1 / xboxTextureScale[1];
xOut.oT2 = oT2 / xboxTextureScale[2];
xOut.oT3 = oT3 / xboxTextureScale[3];
return xOut;
}
)";
EmuCompileShader(hlsl, g_vs_model, &pPassthroughShader, "passthrough.hlsl");
}
*ppHostShader = pPassthroughShader;
return 0;
}

View file

@ -21,5 +21,5 @@ extern HRESULT EmuCompileVertexShader
extern void EmuCompileFixedFunction(ID3DBlob** ppHostShader);
extern void EmuCompileXboxPassthrough(ID3DBlob** ppHostShader);
extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader);

View file

@ -1,16 +1,15 @@
#define LOG_PREFIX CXBXR_MODULE::VSHCACHE
#include "VertexShaderCache.h"
#include "VertexShaderSource.h"
#include "core/kernel/init/CxbxKrnl.h"
#include "util/hasher.h"
#include "core/kernel/support/Emu.h"
VertexShaderCache g_VertexShaderCache = VertexShaderCache();
VertexShaderSource g_VertexShaderSource = VertexShaderSource();
// FIXME : This should really be released and created in step with the D3D device lifecycle rather than being a thing on its own
// (And the ResetD3DDevice method should be removed)
ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, ShaderKey key) {
ID3DBlob* pCompiledShader;
@ -26,7 +25,7 @@ ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, S
// Find a shader
// Return true if the shader was found
bool VertexShaderCache::_FindShader(ShaderKey key, LazyVertexShader** ppLazyShader) {
bool VertexShaderSource::_FindShader(ShaderKey key, LazyVertexShader** ppLazyShader) {
auto it = cache.find(key);
if (it == cache.end()) {
// We didn't find anything! Was CreateShader called?
@ -40,7 +39,7 @@ ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, S
// Create a new shader
// If the shader was already created, just increase its reference count
ShaderKey VertexShaderCache::CreateShader(const xbox::dword_xt* pXboxFunction, DWORD *pXboxFunctionSize) {
ShaderKey VertexShaderSource::CreateShader(const xbox::dword_xt* pXboxFunction, DWORD *pXboxFunctionSize) {
IntermediateVertexShader intermediateShader;
*pXboxFunctionSize = GetVshFunctionSize(pXboxFunction);
@ -87,7 +86,7 @@ ShaderKey VertexShaderCache::CreateShader(const xbox::dword_xt* pXboxFunction, D
}
// Get a shader using the given key
IDirect3DVertexShader* VertexShaderCache::GetShader(ShaderKey key)
IDirect3DVertexShader* VertexShaderSource::GetShader(ShaderKey key)
{
LazyVertexShader* pLazyShader = nullptr;
@ -114,12 +113,6 @@ IDirect3DVertexShader* VertexShaderCache::GetShader(ShaderKey key)
EmuLog(LOG_LEVEL::DEBUG, "Waiting for shader %llx...", key);
pCompiledShader = pLazyShader->compileResult.get();
if (!pCompiledShader) {
EmuLog(LOG_LEVEL::ERROR2, "Failed to compile vertex shader for %llx", key);
pLazyShader->isReady = true;
return nullptr;
}
// Create the shader
auto hRet = pD3DDevice->CreateVertexShader
(
@ -152,7 +145,7 @@ IDirect3DVertexShader* VertexShaderCache::GetShader(ShaderKey key)
}
// Release a shader. Doesn't actually release any resources for now
void VertexShaderCache::ReleaseShader(ShaderKey key)
void VertexShaderSource::ReleaseShader(ShaderKey key)
{
// For now, don't bother releasing any shaders
LazyVertexShader* pLazyShader;
@ -172,25 +165,8 @@ void VertexShaderCache::ReleaseShader(ShaderKey key)
}
}
void VertexShaderCache::ResetD3DDevice(IDirect3DDevice9* newDevice)
void VertexShaderSource::ResetD3DDevice(IDirect3DDevice9* newDevice)
{
EmuLog(LOG_LEVEL::DEBUG, "Resetting D3D device");
cache.clear();
this->pD3DDevice = newDevice;
}
void VertexShaderCache::Clear()
{
for (auto& x : cache) {
if (!x.second.isReady) {
auto pBlob = x.second.compileResult.get();
if (pBlob) {
pBlob->Release();
}
}
else if(x.second.pHostVertexShader) {
x.second.pHostVertexShader->Release();
}
}
cache.clear();
}

View file

@ -8,7 +8,7 @@
typedef uint64_t ShaderKey;
// Manages creation and caching of vertex shaders
class VertexShaderCache {
class VertexShaderSource {
public:
ShaderKey CreateShader(const xbox::dword_xt* pXboxFunction, DWORD* pXboxFunctionSize);
@ -16,7 +16,6 @@ public:
void ReleaseShader(ShaderKey key);
void ResetD3DDevice(IDirect3DDevice9* pD3DDevice);
void Clear();
// TODO
// WriteCacheToDisk
@ -43,6 +42,6 @@ private:
bool _FindShader(ShaderKey key, LazyVertexShader** ppLazyShader);
};
extern VertexShaderCache g_VertexShaderCache;
extern VertexShaderSource g_VertexShaderSource;
#endif

View file

@ -39,7 +39,6 @@
#include "core\kernel\support\Emu.h"
#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_pD3DDevice, g_pXbox_PixelShader
#include "core\hle\D3D8\Direct3D9\Shader.h" // For g_ShaderSources
#include "core\hle\D3D8\XbPixelShader.h"
#include "core\hle\D3D8\Direct3D9\PixelShader.h" // EmuCompilePixelShader
#include "core\hle\D3D8\XbD3D8Logging.h" // For D3DErrorString()
@ -52,7 +51,6 @@
#include <assert.h> // assert()
#include <process.h>
#include <locale.h>
#include <filesystem>
#include <fstream>
#include <sstream>
@ -661,12 +659,35 @@ constexpr int PSH_XBOX_CONSTANT_BEM = PSH_XBOX_CONSTANT_FOG + 1; // = 19..22
constexpr int PSH_XBOX_CONSTANT_LUM = PSH_XBOX_CONSTANT_BEM + 4; // = 23..26
// Which winding order to consider as the front face
constexpr int PSH_XBOX_CONSTANT_FRONTFACE_FACTOR = PSH_XBOX_CONSTANT_LUM + 4; // = 27
// Fog table information {Table mode, density, start and end}
constexpr int CXBX_D3DPS_CONSTREG_FOGINFO = PSH_XBOX_CONSTANT_FRONTFACE_FACTOR + 1; // = 28
//Fog enable flag
constexpr int PSH_XBOX_CONSTANT_FOGENABLE = CXBX_D3DPS_CONSTREG_FOGINFO + 1; // = 29
// This concludes the set of constants that need to be set on host :
constexpr int PSH_XBOX_CONSTANT_MAX = PSH_XBOX_CONSTANT_FOGENABLE + 1; // = 30
constexpr int PSH_XBOX_CONSTANT_MAX = PSH_XBOX_CONSTANT_FRONTFACE_FACTOR + 1; // = 28
std::string GetFixedFunctionShaderTemplate() {
static bool loaded = false;
static std::string hlslString;
// TODO does this need to be thread safe?
if (!loaded) {
loaded = true;
// Determine the filename and directory for the fixed function shader
// TODO make this a relative path so we guarantee an LPCSTR for D3DCompile
auto hlslDir = std::filesystem::path(szFilePath_CxbxReloaded_Exe)
.parent_path()
.append("hlsl");
auto sourceFile = hlslDir.append("FixedFunctionPixelShader.hlsl").string();
// Load the shader into a string
std::ifstream hlslStream(sourceFile);
std::stringstream hlsl;
hlsl << hlslStream.rdbuf();
hlslString = hlsl.str();
}
return hlslString;
}
std::string_view GetD3DTOPString(int d3dtop) {
static constexpr std::string_view opToString[] = {
@ -768,21 +789,6 @@ IDirect3DPixelShader9* GetFixedFunctionShader()
// TODO move this cache elsewhere - and flush it when the device is released!
static std::unordered_map<uint64_t, IDirect3DPixelShader9*> ffPsCache = {};
// Support hotloading hlsl
static int pixelShaderVersion = -1;
int shaderVersion = g_ShaderSources.Update();
if (pixelShaderVersion != shaderVersion) {
pixelShaderVersion = shaderVersion;
g_pD3DDevice->SetPixelShader(nullptr);
for (auto& hostShader : ffPsCache) {
if (hostShader.second)
hostShader.second->Release();
}
ffPsCache.clear();
}
// Create a key from state that will be baked in to the shader
PsTextureHardcodedState states[4] = {};
int sampleType[4] = { SAMPLE_NONE, SAMPLE_NONE, SAMPLE_NONE, SAMPLE_NONE };
@ -865,74 +871,68 @@ IDirect3DPixelShader9* GetFixedFunctionShader()
}
// Build and compile a new shader
std::string hlslTemplate = g_ShaderSources.fixedFunctionPixelShaderHlsl;
auto hlslTemplate = GetFixedFunctionShaderTemplate();
// In D3D9 it seems we need to know hardcode if we're doing a 2D or 3D lookup
const std::string sampleTypePattern = "TEXTURE_SAMPLE_TYPE;";
auto sampleTypeReplace = hlslTemplate.find(sampleTypePattern);
std::string finalShader = hlslTemplate;
if (sampleTypeReplace != std::string::npos) {
static constexpr std::string_view typeToString[] = {
"SAMPLE_NONE",
"SAMPLE_2D",
"SAMPLE_3D",
"SAMPLE_CUBE"
};
static constexpr std::string_view typeToString[] = {
"SAMPLE_NONE",
"SAMPLE_2D",
"SAMPLE_3D",
"SAMPLE_CUBE"
};
std::stringstream sampleTypeString;
sampleTypeString << "{"
<< typeToString[sampleType[0]] << ", "
<< typeToString[sampleType[1]] << ", "
<< typeToString[sampleType[2]] << ", "
<< typeToString[sampleType[3]] << "};";
std::stringstream sampleTypeString;
sampleTypeString << "{"
<< typeToString[sampleType[0]] << ", "
<< typeToString[sampleType[1]] << ", "
<< typeToString[sampleType[2]] << ", "
<< typeToString[sampleType[3]] << "};";
finalShader = hlslTemplate.replace(sampleTypeReplace, sampleTypePattern.size(), sampleTypeString.str());
}
auto finalShader = hlslTemplate.replace(sampleTypeReplace, sampleTypePattern.size(), sampleTypeString.str());
// Hardcode the texture stage operations and arguments
// So the shader handles exactly one combination of values
const std::string stageDef = "// STAGE DEFINITIONS";
auto stageDefInsert = finalShader.find(stageDef);
if (stageDefInsert != std::string::npos) {
stageDefInsert += stageDef.size();
auto stageDefInsert = finalShader.find(stageDef) + stageDef.size();
std::stringstream stageSetup;
stageSetup << '\n';
std::stringstream stageSetup;
stageSetup << '\n';
for (int i = 0; i < 4; i++) {
for (int i = 0; i < 4; i++) {
#ifdef ENABLE_FF_ALPHAKILL
// Even when a stage is disabled, we still have to fully initialize it's values, to prevent
// "error X4000: variable 'stages' used without having been completely initialized"
// Even when a stage is disabled, we still have to fully initialize it's values, to prevent
// "error X4000: variable 'stages' used without having been completely initialized"
#else
// The stage is initialized to be disabled
// We don't have to output anything
if (states[i].COLOROP == X_D3DTOP_DISABLE)
continue;
// The stage is initialized to be disabled
// We don't have to output anything
if (states[i].COLOROP == X_D3DTOP_DISABLE)
continue;
#endif
std::string target = "stages[" + std::to_string(i) + "].";
std::string target = "stages[" + std::to_string(i) + "].";
auto s = states[i];
stageSetup << target << "COLOROP = " << GetD3DTOPString(s.COLOROP) << ";\n";
auto s = states[i];
stageSetup << target << "COLOROP = " << GetD3DTOPString(s.COLOROP) << ";\n";
stageSetup << target << "COLORARG0 = " << GetD3DTASumString(s.COLORARG0) << ";\n";
stageSetup << target << "COLORARG1 = " << GetD3DTASumString(s.COLORARG1) << ";\n";
stageSetup << target << "COLORARG2 = " << GetD3DTASumString(s.COLORARG2) << ";\n";
stageSetup << target << "COLORARG0 = " << GetD3DTASumString(s.COLORARG0) << ";\n";
stageSetup << target << "COLORARG1 = " << GetD3DTASumString(s.COLORARG1) << ";\n";
stageSetup << target << "COLORARG2 = " << GetD3DTASumString(s.COLORARG2) << ";\n";
stageSetup << target << "ALPHAOP = " << GetD3DTOPString(s.ALPHAOP) << ";\n";
stageSetup << target << "ALPHAOP = " << GetD3DTOPString(s.ALPHAOP) << ";\n";
stageSetup << target << "ALPHAARG0 = " << GetD3DTASumString(s.ALPHAARG0) << ";\n";
stageSetup << target << "ALPHAARG1 = " << GetD3DTASumString(s.ALPHAARG1) << ";\n";
stageSetup << target << "ALPHAARG2 = " << GetD3DTASumString(s.ALPHAARG2) << ";\n";
stageSetup << target << "ALPHAARG0 = " << GetD3DTASumString(s.ALPHAARG0) << ";\n";
stageSetup << target << "ALPHAARG1 = " << GetD3DTASumString(s.ALPHAARG1) << ";\n";
stageSetup << target << "ALPHAARG2 = " << GetD3DTASumString(s.ALPHAARG2) << ";\n";
stageSetup << target << "RESULTARG = " << GetD3DTASumString(s.RESULTARG, false) << ";\n";
stageSetup << '\n';
}
finalShader = finalShader.insert(stageDefInsert, stageSetup.str());
stageSetup << target << "RESULTARG = " << GetD3DTASumString(s.RESULTARG, false) << ";\n";
stageSetup << '\n';
}
finalShader = finalShader.insert(stageDefInsert, stageSetup.str());
// Compile the shader
ID3DBlob* pShaderBlob;
@ -944,15 +944,12 @@ IDirect3DPixelShader9* GetFixedFunctionShader()
auto pseudoSourceFile = hlslDir.append(pseudoFileName).string();
EmuCompileShader(finalShader, "ps_3_0", &pShaderBlob, pseudoSourceFile.c_str());
// Create shader object for the device
IDirect3DPixelShader9* pShader = nullptr;
if (pShaderBlob) {
// Create shader object for the device
auto hRet = g_pD3DDevice->CreatePixelShader((DWORD*)pShaderBlob->GetBufferPointer(), &pShader);
if (hRet != S_OK) {
EmuLog(LOG_LEVEL::ERROR2, "Failed to compile fixed function pixel shader");
}
pShaderBlob->Release();
}
auto hRet = g_pD3DDevice->CreatePixelShader((DWORD*)pShaderBlob->GetBufferPointer(), &pShader);
if (hRet != S_OK)
CxbxrAbort("Failed to compile fixed function pixel shader");
pShaderBlob->Release();
// Insert the shader into the cache
ffPsCache[key] = pShader;
@ -975,10 +972,7 @@ void UpdateFixedFunctionPixelShaderState()
ffPsState.SpecularEnable = XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_SPECULARENABLE);
ffPsState.FogEnable = XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_FOGENABLE);
ffPsState.FogColor = (D3DXVECTOR3)((D3DXCOLOR)XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_FOGCOLOR));
ffPsState.FogTableMode = XboxRenderStates.GetXboxRenderState(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGTABLEMODE);
ffPsState.FogDensity = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGDENSITY);
ffPsState.FogStart = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGSTART);
ffPsState.FogEnd = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGEND);
// Texture state
for (int i = 0; i < xbox::X_D3DTS_STAGECOUNT; i++) {
@ -1035,21 +1029,6 @@ void DxbxUpdateActivePixelShader() // NOPATCH
// Fetch all other values that are used in the IsEquivalent check :
CompletePSDef.SnapshotRuntimeVariables();
// Support hotloading hlsl
static int pixelShaderVersion = -1;
int shaderVersion = g_ShaderSources.Update();
if (pixelShaderVersion != shaderVersion) {
pixelShaderVersion = shaderVersion;
g_pD3DDevice->SetPixelShader(nullptr);
for (auto& hostShader : g_RecompiledPixelShaders) {
if (hostShader.ConvertedPixelShader)
hostShader.ConvertedPixelShader->Release();
}
g_RecompiledPixelShaders.clear();
}
// Now, see if we already have a shader compiled for this definition :
// TODO : Change g_RecompiledPixelShaders into an unordered_map, hash just the identifying PSDef members, and add cache eviction (clearing host resources when pruning)
const PSH_RECOMPILED_SHADER* RecompiledPixelShader = nullptr;
@ -1171,18 +1150,7 @@ void DxbxUpdateActivePixelShader() // NOPATCH
frontfaceFactor = cwFrontface ? 1.0 : -1.0;
}
fColor[PSH_XBOX_CONSTANT_FRONTFACE_FACTOR].r = frontfaceFactor;
float fogEnable = XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_FOGENABLE) > 0;
const float fogTableMode = XboxRenderStates.GetXboxRenderState(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGTABLEMODE);
const float fogDensity = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGDENSITY);
const float fogStart = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGSTART);
const float fogEnd = XboxRenderStates.GetXboxRenderStateAsFloat(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGEND);
fColor[CXBX_D3DPS_CONSTREG_FOGINFO].r = fogTableMode;
fColor[CXBX_D3DPS_CONSTREG_FOGINFO].g = fogDensity;
fColor[CXBX_D3DPS_CONSTREG_FOGINFO].b = fogStart;
fColor[CXBX_D3DPS_CONSTREG_FOGINFO].a = fogEnd;
fColor[PSH_XBOX_CONSTANT_FOGENABLE].r = fogEnable;
// Assume all constants are in use (this is much easier than tracking them for no other purpose than to skip a few here)
// Read the color from the corresponding render state slot :
// Set all host constant values using a single call:

View file

@ -469,15 +469,12 @@ extern void EmuExecutePushBufferRaw
LOG_TEST_CASE("Pushbuffer COMMAND_TYPE_JUMP_LONG");
dma_get_jmp_shadow = dma_get;
dma_get = (uint32_t *)(CONTIGUOUS_MEMORY_BASE | (word & COMMAND_WORD_MASK_JUMP_LONG));
//NV2A uses COMMAND_TYPE_JUMP_LONG as return for COMMAND_TYPE_CALL. we clear the subr_active here to indicate we have returned from COMMAND_TYPE_CALL.
subr_active = false;
continue; // while
case COMMAND_TYPE_CALL: // Note : NV2A return is said not to work?
if (subr_active) {
LOG_TEST_CASE("Pushbuffer COMMAND_TYPE_CALL while another call was active!");
// TODO : throw DMA_PUSHER(CALL_SUBR_ACTIVE);
// For now, don't even attempt to run through, this should never happened, if it happened, the pgraph handler will go crazy.
CxbxrAbort("Pushbuffer COMMAND_TYPE_CALL called without return!");
return; // For now, don't even attempt to run through
}
else {
LOG_TEST_CASE("Pushbuffer COMMAND_TYPE_CALL");

View file

@ -34,8 +34,7 @@
#include "core\kernel\support\Emu.h"
#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_Xbox_VertexShader_Handle
#include "core\hle\D3D8\Direct3D9\RenderStates.h" // For XboxRenderStateConverter
#include "core\hle\D3D8\Direct3D9\VertexShaderCache.h" // For g_VertexShaderCache
#include "core\hle\D3D8\Direct3D9\Shader.h" // For g_ShaderSources
#include "core\hle\D3D8\Direct3D9\VertexShaderSource.h" // For g_VertexShaderSource
#include "core\hle\D3D8\XbVertexBuffer.h" // For CxbxImpl_SetVertexData4f
#include "core\hle\D3D8\XbVertexShader.h"
#include "core\hle\D3D8\XbD3D8Logging.h" // For DEBUG_D3DRESULT
@ -50,7 +49,6 @@
#include <unordered_map>
#include <array>
#include <bitset>
#include <filesystem>
// External symbols :
extern xbox::X_STREAMINPUT g_Xbox_SetStreamSource[X_VSH_MAX_STREAMS]; // Declared in XbVertexBuffer.cpp
@ -1126,49 +1124,10 @@ IDirect3DVertexDeclaration* CxbxCreateHostVertexDeclaration(D3DVERTEXELEMENT *pD
return pHostVertexDeclaration;
}
IDirect3DVertexShader* InitShader(void (*compileFunc)(ID3DBlob**), const char* label) {
IDirect3DVertexShader* shader = nullptr;
ID3DBlob* pBlob = nullptr;
compileFunc(&pBlob);
if (pBlob) {
HRESULT hRet = g_pD3DDevice->CreateVertexShader((DWORD*)pBlob->GetBufferPointer(), &shader);
pBlob->Release();
if (FAILED(hRet)) CxbxrAbort("Failed to create shader: %s", label);
}
return shader;
}
static IDirect3DVertexShader* passthroughshader;
void CxbxUpdateHostVertexShader()
{
extern bool g_bUsePassthroughHLSL; // TMP glue
// TODO: move d3d9 state to VertexShader.cpp
static IDirect3DVertexShader* fixedFunctionShader = nullptr; // TODO: move to shader cache
static IDirect3DVertexShader* passthroughShader = nullptr;
static int vertexShaderVersion = -1;
int shaderVersion = g_ShaderSources.Update();
if (vertexShaderVersion != shaderVersion) {
vertexShaderVersion = shaderVersion;
g_pD3DDevice->SetVertexShader(nullptr);
EmuLog(LOG_LEVEL::INFO, "Loading vertex shaders...");
g_VertexShaderCache.Clear();
if (fixedFunctionShader) {
fixedFunctionShader->Release();
fixedFunctionShader = nullptr;
}
fixedFunctionShader = InitShader(EmuCompileFixedFunction, "Fixed Function Vertex Shader");
if (passthroughShader) {
passthroughShader->Release();
passthroughShader = nullptr;
}
passthroughShader = InitShader(EmuCompileXboxPassthrough, "Passthrough Vertex Shader");
}
// TODO Call this when state is dirty
// Rather than every time state changes
@ -1176,20 +1135,43 @@ void CxbxUpdateHostVertexShader()
LOG_INIT; // Allows use of DEBUG_D3DRESULT
if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction) {
HRESULT hRet = g_pD3DDevice->SetVertexShader(fixedFunctionShader);
IDirect3DVertexShader* fixedFunctionShader = nullptr;
HRESULT hRet;
if (g_UseFixedFunctionVertexShader) {
static IDirect3DVertexShader* ffHlsl = nullptr;
if (ffHlsl == nullptr) {
ID3DBlob* pBlob = nullptr;
EmuCompileFixedFunction(&pBlob);
if (pBlob) {
hRet = g_pD3DDevice->CreateVertexShader((DWORD*)pBlob->GetBufferPointer(), &ffHlsl);
if (FAILED(hRet)) CxbxrAbort("Failed to create fixed-function shader");
}
}
fixedFunctionShader = ffHlsl;
}
hRet = g_pD3DDevice->SetVertexShader(fixedFunctionShader);
if (FAILED(hRet)) CxbxrAbort("Failed to set fixed-function shader");
}
else if (g_Xbox_VertexShaderMode == VertexShaderMode::Passthrough && g_bUsePassthroughHLSL) {
HRESULT hRet = g_pD3DDevice->SetVertexShader(passthroughShader);
if (FAILED(hRet)) CxbxrAbort("Failed to set passthrough shader");
if (passthroughshader == nullptr) {
ID3DBlob* pBlob = nullptr;
EmuCompileXboxPassthrough(&pBlob);
if (pBlob) {
g_pD3DDevice->CreateVertexShader((DWORD*)pBlob->GetBufferPointer(), &passthroughshader);
}
}
HRESULT hRet = g_pD3DDevice->SetVertexShader(passthroughshader);
}
else {
auto pTokens = GetCxbxVertexShaderSlotPtr(g_Xbox_VertexShader_FunctionSlots_StartAddress);
assert(pTokens);
// Create a vertex shader from the tokens
DWORD shaderSize;
auto VertexShaderKey = g_VertexShaderCache.CreateShader(pTokens, &shaderSize);
IDirect3DVertexShader* pHostVertexShader = g_VertexShaderCache.GetShader(VertexShaderKey);
auto VertexShaderKey = g_VertexShaderSource.CreateShader(pTokens, &shaderSize);
IDirect3DVertexShader* pHostVertexShader = g_VertexShaderSource.GetShader(VertexShaderKey);
HRESULT hRet = g_pD3DDevice->SetVertexShader(pHostVertexShader);
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader");
}
@ -1577,7 +1559,7 @@ void CxbxImpl_DeleteVertexShader(DWORD Handle)
RegisterCxbxVertexDeclaration(pCxbxVertexDeclaration->Key, nullptr); // Remove from cache (which will free present pCxbxVertexDeclaration)
// Release the host vertex shader
g_VertexShaderCache.ReleaseShader(pCxbxVertexShader->Key);
g_VertexShaderSource.ReleaseShader(pCxbxVertexShader->Key);
#endif
}

View file

@ -53,7 +53,6 @@
// TODO: Move these to LLE APUDevice once we have one!
static constexpr uint32_t APU_TIMER_FREQUENCY = 48000;
static uint64_t dsound_last;
uint32_t GetAPUTime()
{
@ -86,6 +85,10 @@ uint32_t GetAPUTime()
there is chance of failure which contain value greater than 0.
*/
// Managed memory xbox audio variables
static std::thread dsound_thread;
static void dsound_thread_worker(LPVOID);
#include "DirectSoundInline.hpp"
#ifdef __cplusplus
@ -95,7 +98,6 @@ extern "C" {
void CxbxInitAudio()
{
g_EmuShared->GetAudioSettings(&g_XBAudio);
dsound_last = get_now();
}
#ifdef __cplusplus
@ -123,6 +125,10 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(DirectSoundCreate)
HRESULT hRet = DS_OK;
if (!initialized) {
dsound_thread = std::thread(dsound_thread_worker, nullptr);
}
// Set this flag when this function is called
g_bDSoundCreateCalled = TRUE;
@ -448,51 +454,51 @@ void StreamBufferAudio(xbox::XbHybridDSBuffer* pHybridBuffer, float msToCopy) {
}
}
void dsound_async_worker()
static void dsound_thread_worker(LPVOID nullPtr)
{
DSoundMutexGuardLock;
g_AffinityPolicy->SetAffinityOther();
xbox::LARGE_INTEGER getTime;
xbox::KeQuerySystemTime(&getTime);
DirectSoundDoWork_Stream(getTime);
}
const int dsStreamInterval = 300;
int waitCounter = 0;
void dsound_worker()
{
// Testcase: Gauntlet Dark Legacy, if Sleep(1) then intro videos start to starved often
// unless console is open with logging enabled. This is the cause of stopping intro videos often.
while (true) {
// FIXME time this loop more accurately
// and account for variation in the length of Sleep calls
// Enforce mutex guard lock only occur inside below bracket for proper compile build.
DSoundMutexGuardLock;
// Testcase: Gauntlet Dark Legacy, if Sleep(1) then intro videos start to starved often
// unless console is open with logging enabled. This is the cause of stopping intro videos often.
Sleep(g_dsBufferStreaming.streamInterval);
waitCounter += g_dsBufferStreaming.streamInterval;
// Stream sound buffer audio
// because the title may change the content of sound buffers at any time
for (auto& pBuffer : g_pDSoundBufferCache) {
// Avoid expensive calls to DirectSound on buffers unless they've been played at least once
// Since some titles create a large amount of buffers, but only use a few
if (pBuffer->emuDSBuffer->EmuStreamingInfo.playRequested) {
DWORD status;
HRESULT hRet = pBuffer->emuDSBuffer->EmuDirectSoundBuffer8->GetStatus(&status);
if (hRet == 0 && status & DSBSTATUS_PLAYING) {
auto streamMs = g_dsBufferStreaming.streamInterval + g_dsBufferStreaming.streamAhead;
StreamBufferAudio(pBuffer, streamMs);
// Enforce mutex guard lock only occur inside below bracket for proper compile build.
{
DSoundMutexGuardLock;
if (waitCounter > dsStreamInterval) {
waitCounter = 0;
// For Async process purpose only
xbox::LARGE_INTEGER getTime;
xbox::KeQuerySystemTime(&getTime);
DirectSoundDoWork_Stream(getTime);
}
}
}
}
uint64_t dsound_next(uint64_t now)
{
constexpr uint64_t dsound_period = 300 * 1000;
uint64_t next = dsound_last + dsound_period;
if (now >= next) {
dsound_async_worker();
dsound_last = get_now();
return dsound_period;
// Stream sound buffer audio
// because the title may change the content of sound buffers at any time
for (auto& pBuffer : g_pDSoundBufferCache) {
// Avoid expensive calls to DirectSound on buffers unless they've been played at least once
// Since some titles create a large amount of buffers, but only use a few
if (pBuffer->emuDSBuffer->EmuStreamingInfo.playRequested) {
DWORD status;
HRESULT hRet = pBuffer->emuDSBuffer->EmuDirectSoundBuffer8->GetStatus(&status);
if (hRet == 0 && status & DSBSTATUS_PLAYING) {
auto streamMs = g_dsBufferStreaming.streamInterval + g_dsBufferStreaming.streamAhead;
StreamBufferAudio(pBuffer, streamMs);
}
}
}
}
}
return dsound_last + dsound_period - now; // time remaining until next dsound async event
}
// Kismet given name for RadWolfie's experiment major issue in the mutt.

View file

@ -81,6 +81,3 @@ extern DsBufferStreaming g_dsBufferStreaming;
extern void DirectSoundDoWork_Buffer(xbox::LARGE_INTEGER& time);
extern void DirectSoundDoWork_Stream(xbox::LARGE_INTEGER& time);
extern void dsound_async_worker();
extern void dsound_worker();
extern uint64_t dsound_next(uint64_t now);

View file

@ -34,7 +34,6 @@
#include <cmath>
#include <iomanip> // For std::setfill and std::setw
#include <filesystem>
#include "core\kernel\init\CxbxKrnl.h"
#include "core\kernel\support\Emu.h"
@ -206,7 +205,6 @@ void CDECL EmuOutputMessage(xb_output_message mFlag,
void CDECL EmuRegisterSymbol(const char* library_str,
uint32_t library_flag,
uint32_t xref_index,
const char* symbol_str,
uint32_t func_addr,
uint32_t revision)
@ -440,6 +438,22 @@ void EmuHLEIntercept(Xbe::Header *pXbeHeader)
<< " -> " << functionName << "\n";
std::printf(output.str().c_str());
}
// Fix up Render state and Texture States
if (g_SymbolAddresses.find("D3DDeferredRenderState") == g_SymbolAddresses.end()
|| g_SymbolAddresses["D3DDeferredRenderState"] == 0) {
EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredRenderState was not found!");
}
if (g_SymbolAddresses.find("D3DDeferredTextureState") == g_SymbolAddresses.end()
|| g_SymbolAddresses["D3DDeferredTextureState"] == 0) {
EmuLog(LOG_LEVEL::WARNING, "EmuD3DDeferredTextureState was not found!");
}
if (g_SymbolAddresses.find("D3DDEVICE") == g_SymbolAddresses.end()
|| g_SymbolAddresses["D3DDEVICE"] == 0) {
EmuLog(LOG_LEVEL::WARNING, "D3DDEVICE was not found!");
}
}
}

View file

@ -1,670 +0,0 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2019 Luke Usher <luke.usher@outlook.com>
// *
// * All rights reserved
// *
// ******************************************************************
#define _XBOXKRNL_DEFEXTRN_
#define LOG_PREFIX CXBXR_MODULE::JVS
#undef FIELD_OFFSET // prevent macro redefinition warnings
#include "EmuShared.h"
#include "common\Logging.h"
#include "common\FilePaths.hpp"
#include "core\kernel\init\CxbxKrnl.h"
#include "core\kernel\support\Emu.h"
#include "core\hle\JVS\JVS.h"
#include "core\hle\Intercept.hpp"
#include "devices\chihiro\JvsIo.h"
#include "devices\Xbox.h"
#include <thread>
#pragma warning(disable:4244) // Silence mio compiler warnings
#include <mio/mmap.hpp>
#pragma warning(default:4244)
// Global variables used to store JVS related firmware/eeproms
mio::mmap_sink g_BaseBoardQcFirmware; // QC Microcontroller firmware
mio::mmap_sink g_BaseBoardScFirmware; // SC Microcontroller firmware
mio::mmap_sink g_BaseBoardEeprom; // Config EEPROM
mio::mmap_sink g_BaseBoardBackupMemory; // Backup Memory (high-scores, etc)
typedef struct _baseboard_state_t {
// Switch 1: Horizontal Display, On = Vertical Display
// Switch 2-3: D3D Resolution Configuraton
// Switch 4: 0 = Hardware Vertex Processing, 1 = Software Vertex processing (Causes D3D to fail).. Horizontal frequency?
// Switch 5: Unknown
// Switch 6-8: Connected AV Pack flag
bool DipSwitch[8];
bool TestButton;
bool ServiceButton;
uint8_t JvsSense;
void Reset()
{
// TODO: Make this configurable
DipSwitch[0] = false;
DipSwitch[1] = false;
DipSwitch[2] = true;
DipSwitch[3] = true;
DipSwitch[4] = false;
DipSwitch[5] = true;
DipSwitch[6] = true;
DipSwitch[7] = true;
TestButton = false;
ServiceButton = false;
JvsSense = 0;
}
uint8_t GetAvPack()
{
uint8_t avpack = 0;
// Dip Switches 6,7,8 combine to form the Av Pack ID
// TODO: Verify the order, these might need to be reversed
avpack &= ~((DipSwitch[5] ? 1 : 0) << 2);
avpack &= ~((DipSwitch[6] ? 1 : 0) << 1);
avpack &= ~ (DipSwitch[7] ? 1 : 0);
return avpack;
}
uint8_t GetPINSA()
{
uint8_t PINSA = 0b11101111; // 1 = Off, 0 = On
// Dip Switches 1-3 are set on PINSA bits 0-2
PINSA &= ~ (DipSwitch[0] ? 1 : 0);
PINSA &= ~((DipSwitch[1] ? 1 : 0) << 1);
PINSA &= ~((DipSwitch[2] ? 1 : 0) << 2);
// Bit 3 is currently unknown, so we don't modify that bit
// Dip Switches 4,5 are set on bits 4,5
PINSA &= ~((DipSwitch[3] ? 1 : 0) << 4);
PINSA &= ~((DipSwitch[4] ? 1 : 0) << 5);
// Bit 6 = Test, Bit 7 = Service
PINSA &= ~((TestButton ? 1 : 0) << 6);
PINSA &= ~((ServiceButton ? 1 : 0) << 7);
return PINSA;
}
uint8_t GetPINSB()
{
// PINSB bits 0-1 represent the JVS Sense line
return JvsSense;
}
} baseboard_state_t;
baseboard_state_t ChihiroBaseBoardState = {};
DWORD* g_pPINSA = nullptr; // Qc PINSA Register: Contains Filter Board DIP Switches + Test/Service buttons
DWORD* g_pPINSB = nullptr; // Qc PINSB Register: Contains JVS Sense Pin state
bool JVS_LoadFile(std::string path, mio::mmap_sink& data)
{
FILE* fp = fopen(path.c_str(), "rb");
if (fp == nullptr) {
return false;
}
std::error_code error;
data = mio::make_mmap_sink(path, error);
if (error) {
return false;
}
return true;
}
void JvsInputThread()
{
g_AffinityPolicy->SetAffinityOther(GetCurrentThread());
while (true) {
// This thread is responsible for reading the emulated Baseboard state
// and setting the correct internal variables
ChihiroBaseBoardState.TestButton = GetAsyncKeyState(VK_F1);
ChihiroBaseBoardState.ServiceButton = GetAsyncKeyState(VK_F2);
// Call into the Jvs I/O board update function
g_pJvsIo->Update();
if (g_pPINSA != nullptr) {
*g_pPINSA = ChihiroBaseBoardState.GetPINSA();
}
if (g_pPINSB != nullptr) {
*g_pPINSB = ChihiroBaseBoardState.GetPINSB();
}
}
}
void JVS_Init()
{
// Init Jvs IO board
g_pJvsIo = new JvsIo(&ChihiroBaseBoardState.JvsSense);
std::string romPath = g_DataFilePath + std::string("\\EmuDisk\\Chihiro");
std::string baseBoardQcFirmwarePath = "ic10_g24lc64.bin";
std::string baseBoardScFirmwarePath = "pc20_g24lc64.bin";
std::string baseBoardEepromPath = "ic11_24lc024.bin";
std::string baseBoardBackupRamPath = "backup_ram.bin";
if (!JVS_LoadFile((romPath + "\\" + baseBoardQcFirmwarePath).c_str(), g_BaseBoardQcFirmware)) {
CxbxrAbort("Failed to load base board firmware: %s", baseBoardQcFirmwarePath.c_str());
}
if (!JVS_LoadFile((romPath + "\\" + baseBoardScFirmwarePath).c_str(), g_BaseBoardScFirmware)) {
CxbxrAbort("Failed to load base board qc firmware: %s", baseBoardScFirmwarePath.c_str());
}
if (!JVS_LoadFile((romPath + "\\" + baseBoardEepromPath).c_str(), g_BaseBoardEeprom)) {
CxbxrAbort("Failed to load base board EEPROM: %s", baseBoardEepromPath.c_str());
}
// backup ram is a special case, we can create it automatically if it doesn't exist
if (!std::filesystem::exists(romPath + "\\" + baseBoardBackupRamPath)) {
FILE *fp = fopen((romPath + "\\" + baseBoardBackupRamPath).c_str(), "w");
if (fp == nullptr) {
CxbxrAbort("Could not create Backup File: %s", baseBoardBackupRamPath.c_str());
}
// Create 128kb empty file for backup ram
fseek(fp, (128 * 1024) - 1, SEEK_SET);
fputc('\0', fp);
fclose(fp);
}
if (!JVS_LoadFile((romPath + "\\" + baseBoardBackupRamPath).c_str(), g_BaseBoardBackupMemory)) {
CxbxrAbort("Failed to load base board BACKUP RAM: %s", baseBoardBackupRamPath.c_str());
}
// Determine which version of JVS_SendCommand this title is using and derive the offset
// TODO: Extract this into a function and also locate PINSB
static int JvsSendCommandVersion = -1;
g_pPINSA = nullptr;
g_pPINSB = nullptr;
auto JvsSendCommandOffset1 = (uintptr_t)GetXboxSymbolPointer("JVS_SendCommand");
auto JvsSendCommandOffset2 = (uintptr_t)GetXboxSymbolPointer("JVS_SendCommand2");
auto JvsSendCommandOffset3 = (uintptr_t)GetXboxSymbolPointer("JVS_SendCommand3");
if (JvsSendCommandOffset1) {
JvsSendCommandVersion = 1;
g_pPINSA = *(DWORD**)(JvsSendCommandOffset1 + 0x2A0);
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
}
if (JvsSendCommandOffset2) {
JvsSendCommandVersion = 2;
g_pPINSA = *(DWORD**)(JvsSendCommandOffset2 + 0x312);
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
}
if (JvsSendCommandOffset3) {
JvsSendCommandVersion = 3;
g_pPINSA = *(DWORD**)(JvsSendCommandOffset3 + 0x307);
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
if ((DWORD)g_pPINSA > XBE_MAX_VA) {
// This was invalid, we must have the other varient of SendCommand3 (SEGABOOT)
g_pPINSA = *(DWORD**)(JvsSendCommandOffset3 + 0x302);
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
}
}
// Set state to a sane initial default
ChihiroBaseBoardState.Reset();
// Auto-Patch Chihiro Region Flag to match the desired game
uint8_t &region = (uint8_t &)g_BaseBoardQcFirmware[0x1F00];
auto regionFlags = g_MediaBoard->GetBootId().regionFlags;
// The region of the system can be converted to a game region flag by doing 1 << region
// This gives a bitmask that can be ANDed with the BootID region flags to check the games support
if ((regionFlags & (1 << region)) == 0) {
// The region was not compatible, so we need to patch the region flag
// This avoids "Error 05: This game is not acceptable by main board."
// We use USA,EXPORT,JAPAN to make sure mutiple-language games default to English first
if (regionFlags & MB_CHIHIRO_REGION_FLAG_USA) {
region = 2;
}
else if (regionFlags & MB_CHIHIRO_REGION_FLAG_EXPORT) {
region = 3;
}
else if (regionFlags & MB_CHIHIRO_REGION_FLAG_JAPAN) {
region = 1;
}
}
// Spawn the Chihiro/JVS Input Thread
std::thread(JvsInputThread).detach();
}
DWORD WINAPI xbox::EMUPATCH(JVS_SendCommand)
(
DWORD a1,
DWORD Command,
DWORD a3,
DWORD Length,
DWORD a5,
DWORD a6,
DWORD a7,
DWORD a8
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(a1)
LOG_FUNC_ARG(Command)
LOG_FUNC_ARG(a3)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(a5)
LOG_FUNC_ARG(a6)
LOG_FUNC_ARG(a7)
LOG_FUNC_ARG(a8)
LOG_FUNC_END;
LOG_UNIMPLEMENTED();
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsBACKUP_Read)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy((void*)Buffer, &g_BaseBoardBackupMemory[Offset], Length);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsBACKUP_Write)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy(&g_BaseBoardBackupMemory[Offset], (void*)Buffer, Length);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsEEPROM_Read)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG_OUT(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy((void*)Buffer, &g_BaseBoardEeprom[Offset], Length);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsEEPROM_Write)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG_OUT(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy(&g_BaseBoardEeprom[Offset], (void*)Buffer, Length);
std::error_code error;
g_BaseBoardEeprom.sync(error);
if (error) {
EmuLog(LOG_LEVEL::WARNING, "Couldn't sync EEPROM to disk");
}
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsFirmwareDownload)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG_OUT(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy((void*)Buffer, &g_BaseBoardQcFirmware[Offset], Length);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsFirmwareUpload)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy(&g_BaseBoardQcFirmware[Offset], (void*)Buffer, Length);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsNodeReceivePacket)
(
PUCHAR Buffer,
PDWORD Length,
DWORD a3
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG_OUT(Buffer)
LOG_FUNC_ARG_OUT(Length)
LOG_FUNC_ARG(a3)
LOG_FUNC_END
// Receive the packet from the connected IO board
uint8_t DeviceId = g_pJvsIo->GetDeviceId();
// TODO : "Number of packets received" below might imply multiple packets might need receiving here...
uint16_t payloadSize = (uint16_t)g_pJvsIo->ReceivePacket(&Buffer[6]);
if (payloadSize > 0) {
Buffer[0] = 0; // Empty header byte, ignored
Buffer[1] = 1; // Number of packets received
Buffer[2] = DeviceId;
Buffer[3] = 0; // Unused
*Length = payloadSize + 6;
// Write the payload size header field
*((uint16_t*)&Buffer[4]) = payloadSize; // Packet Length (bytes 4-5)
// TODO : Prevent little/big endian issues here by explicitly setting Buffer[4] and Buffer[5]
}
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsNodeSendPacket)
(
PUCHAR Buffer,
DWORD Length,
DWORD a3
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Buffer)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(a3)
LOG_FUNC_END
// Buffer contains two opening bytes, '00' and 'XX', where XX is the number of JVS packets to send
// Each JVS packet is prepended with a '00' byte, the rest of the packet is as-per the JVS I/O standard.
// Ignore Buffer[0] (should be 0x00)
unsigned packetCount = Buffer[1];
uint8_t* packetPtr = &Buffer[2]; // First JVS packet starts at offset 2;
for (unsigned i = 0; i < packetCount; i++) {
// Skip the separator byte (should be 0x00)
packetPtr++;
// Send the packet to the connected I/O board
size_t bytes = g_pJvsIo->SendPacket(packetPtr);
// Set packetPtr to the next packet
packetPtr += bytes;
}
RETURN(0);
}
// Binary Coded Decimal to Decimal conversion
uint8_t BcdToUint8(uint8_t value)
{
return value - 6 * (value >> 4);
}
uint8_t Uint8ToBcd(uint8_t value)
{
return value + 6 * (value / 10);
}
DWORD WINAPI xbox::EMUPATCH(JvsRTC_Read)
(
DWORD a1,
DWORD a2,
JvsRTCTime* pTime,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(a1)
LOG_FUNC_ARG(a2)
LOG_FUNC_ARG_OUT(time)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
time_t hostTime;
struct tm* hostTimeInfo;
time(&hostTime);
hostTimeInfo = localtime(&hostTime);
memset(pTime, 0, sizeof(JvsRTCTime));
pTime->day = Uint8ToBcd(hostTimeInfo->tm_mday);
pTime->month = Uint8ToBcd(hostTimeInfo->tm_mon + 1); // Chihiro month counter stats at 1
pTime->year = Uint8ToBcd(hostTimeInfo->tm_year - 100); // Chihiro starts counting from year 2000
pTime->hour = Uint8ToBcd(hostTimeInfo->tm_hour);
pTime->minute = Uint8ToBcd(hostTimeInfo->tm_min);
pTime->second = Uint8ToBcd(hostTimeInfo->tm_sec);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsRTC_Write)
(
DWORD a1,
DWORD a2,
JvsRTCTime* pTime,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(a1)
LOG_FUNC_ARG(a2)
LOG_FUNC_ARG_OUT(time)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
LOG_UNIMPLEMENTED();
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsScFirmwareDownload)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG_OUT(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy((void*)Buffer, &g_BaseBoardScFirmware[Offset], Length);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsScFirmwareUpload)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy(&g_BaseBoardScFirmware[Offset], (void*)Buffer, Length);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsScReceiveMidi)
(
DWORD a1,
DWORD a2,
DWORD a3
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(a1)
LOG_FUNC_ARG(a2)
LOG_FUNC_ARG(a3)
LOG_FUNC_END
LOG_UNIMPLEMENTED();
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsScSendMidi)
(
DWORD a1,
DWORD a2,
DWORD a3
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(a1)
LOG_FUNC_ARG(a2)
LOG_FUNC_ARG(a3)
LOG_FUNC_END
LOG_UNIMPLEMENTED();
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsScReceiveRs323c)
(
PUCHAR Buffer,
DWORD Length,
DWORD a3
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Buffer)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(a3)
LOG_FUNC_END
LOG_UNIMPLEMENTED();
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsScSendRs323c)
(
PUCHAR Buffer,
DWORD Length,
DWORD a3
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Buffer)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(a3)
LOG_FUNC_END
LOG_UNIMPLEMENTED();
RETURN(0);
}

View file

@ -1,182 +0,0 @@
// ******************************************************************
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2019 Luke Usher <luke.usher@outlook.com>
// *
// * All rights reserved
// *
// ******************************************************************
#ifndef JVS_H
#define JVS_H
// Used by CxbxKrnl to setup JVS roms
void JVS_Init();
#include "core\hle\XAPI\Xapi.h" // For EMUPATCH
namespace xbox {
DWORD WINAPI EMUPATCH(JVS_SendCommand)
(
DWORD a1,
DWORD Command,
DWORD a3,
DWORD Length,
DWORD a5,
DWORD a6,
DWORD a7,
DWORD a8
);
DWORD WINAPI EMUPATCH(JvsBACKUP_Read)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
);
DWORD WINAPI EMUPATCH(JvsBACKUP_Write)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
);
DWORD WINAPI EMUPATCH(JvsEEPROM_Read)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
);
DWORD WINAPI EMUPATCH(JvsEEPROM_Write)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
);
DWORD WINAPI EMUPATCH(JvsFirmwareDownload)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
);
DWORD WINAPI EMUPATCH(JvsFirmwareUpload)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
);
DWORD WINAPI EMUPATCH(JvsNodeReceivePacket)
(
PUCHAR Buffer,
PDWORD Length,
DWORD a3
);
DWORD WINAPI EMUPATCH(JvsNodeSendPacket)
(
PUCHAR Buffer,
DWORD Length,
DWORD a3
);
typedef struct {
UCHAR second;
UCHAR minute;
UCHAR hour;
UCHAR unused_2;
UCHAR day;
UCHAR month;
UCHAR year;
UCHAR unused_1;
} JvsRTCTime;
DWORD WINAPI EMUPATCH(JvsRTC_Read)
(
DWORD a1,
DWORD a2,
JvsRTCTime *time,
DWORD a4
);
DWORD WINAPI EMUPATCH(JvsRTC_Write)
(
DWORD a1,
DWORD a2,
JvsRTCTime *time,
DWORD a4
);
DWORD WINAPI EMUPATCH(JvsScFirmwareDownload)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
);
DWORD WINAPI EMUPATCH(JvsScFirmwareUpload)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
);
DWORD WINAPI EMUPATCH(JvsScReceiveMidi)
(
DWORD a1,
DWORD a2,
DWORD a3
);
DWORD WINAPI EMUPATCH(JvsScSendMidi)
(
DWORD a1,
DWORD a2,
DWORD a3
);
DWORD WINAPI EMUPATCH(JvsScReceiveRs323c)
(
PUCHAR Buffer,
DWORD Length,
DWORD a3
);
DWORD WINAPI EMUPATCH(JvsScSendRs323c)
(
PUCHAR Buffer,
DWORD Length,
DWORD a3
);
}
#endif

View file

@ -29,7 +29,6 @@
#include "core\kernel\init\CxbxKrnl.h"
#include "core\kernel\support\Emu.h"
#include "core\hle\D3D8\Direct3D9/Direct3D9.h"
#include "core\hle\JVS\JVS.h"
#include "core\hle\DSOUND\DirectSound\DirectSound.hpp"
#include "Patches.hpp"
#include "Intercept.hpp"
@ -58,16 +57,13 @@ const uint32_t PATCH_IS_FIBER = 1 << 4;
std::map<const std::string, const xbox_patch_t> g_PatchTable = {
// Direct3D
PATCH_ENTRY("CDevice_SetStateUP", xbox::EMUPATCH(CDevice_SetStateUP), PATCH_HLE_D3D),
PATCH_ENTRY("CDevice_SetStateUP_4", xbox::EMUPATCH(CDevice_SetStateUP_4), PATCH_HLE_D3D),
PATCH_ENTRY("CDevice_SetStateUP_0__LTCG_esi1", xbox::EMUPATCH(CDevice_SetStateUP_0__LTCG_esi1), PATCH_HLE_D3D),
PATCH_ENTRY("CDevice_SetStateVB", xbox::EMUPATCH(CDevice_SetStateVB), PATCH_HLE_D3D),
PATCH_ENTRY("CDevice_SetStateVB_8", xbox::EMUPATCH(CDevice_SetStateVB_8), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_Begin", xbox::EMUPATCH(D3DDevice_Begin), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_BeginPush_4", xbox::EMUPATCH(D3DDevice_BeginPush_4), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_BeginPush_8", xbox::EMUPATCH(D3DDevice_BeginPush_8), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_BeginPush", xbox::EMUPATCH(D3DDevice_BeginPush), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_BeginPush2", xbox::EMUPATCH(D3DDevice_BeginPush2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_BeginVisibilityTest", xbox::EMUPATCH(D3DDevice_BeginVisibilityTest), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_BlockOnFence", xbox::EMUPATCH(D3DDevice_BlockOnFence), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_BlockUntilVerticalBlank", xbox::EMUPATCH(D3DDevice_BlockUntilVerticalBlank), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_BlockUntilVerticalBlank", xbox::EMUPATCH(D3DDevice_BlockUntilVerticalBlank), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_Clear", xbox::EMUPATCH(D3DDevice_Clear), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_CopyRects", xbox::EMUPATCH(D3DDevice_CopyRects), PATCH_HLE_D3D),
// PATCH_ENTRY("D3DDevice_CreateVertexShader", xbox::EMUPATCH(D3DDevice_CreateVertexShader), PATCH_HLE_D3D),
@ -78,10 +74,9 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
PATCH_ENTRY("D3DDevice_DrawRectPatch", xbox::EMUPATCH(D3DDevice_DrawRectPatch), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawTriPatch", xbox::EMUPATCH(D3DDevice_DrawTriPatch), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawVertices", xbox::EMUPATCH(D3DDevice_DrawVertices), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawVertices_4__LTCG_ecx2_eax3", xbox::EMUPATCH(D3DDevice_DrawVertices_4__LTCG_ecx2_eax3), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawVertices_8__LTCG_eax3", xbox::EMUPATCH(D3DDevice_DrawVertices_8__LTCG_eax3), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawVertices_4", xbox::EMUPATCH(D3DDevice_DrawVertices_4), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawVerticesUP", xbox::EMUPATCH(D3DDevice_DrawVerticesUP), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawVerticesUP_12__LTCG_ebx3", xbox::EMUPATCH(D3DDevice_DrawVerticesUP_12__LTCG_ebx3), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawVerticesUP_12", xbox::EMUPATCH(D3DDevice_DrawVerticesUP_12), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_EnableOverlay", xbox::EMUPATCH(D3DDevice_EnableOverlay), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_End", xbox::EMUPATCH(D3DDevice_End), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_EndPush", xbox::EMUPATCH(D3DDevice_EndPush), PATCH_HLE_D3D),
@ -123,14 +118,12 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
PATCH_ENTRY("D3DDevice_Present", xbox::EMUPATCH(D3DDevice_Present), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_PrimeVertexCache", xbox::EMUPATCH(D3DDevice_PrimeVertexCache), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_Reset", xbox::EMUPATCH(D3DDevice_Reset), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_Reset_0__LTCG_edi1", xbox::EMUPATCH(D3DDevice_Reset_0__LTCG_edi1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_Reset_0__LTCG_ebx1", xbox::EMUPATCH(D3DDevice_Reset_0__LTCG_ebx1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_RunPushBuffer", xbox::EMUPATCH(D3DDevice_RunPushBuffer), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_RunVertexStateShader", xbox::EMUPATCH(D3DDevice_RunVertexStateShader), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SelectVertexShader", xbox::EMUPATCH(D3DDevice_SelectVertexShader), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_SelectVertexShaderDirect", xbox::EMUPATCH(D3DDevice_SelectVertexShaderDirect), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2", xbox::EMUPATCH(D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SelectVertexShader_4__LTCG_eax1", xbox::EMUPATCH(D3DDevice_SelectVertexShader_4__LTCG_eax1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SelectVertexShader_0", xbox::EMUPATCH(D3DDevice_SelectVertexShader_0), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SelectVertexShader_4", xbox::EMUPATCH(D3DDevice_SelectVertexShader_4), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetBackBufferScale", xbox::EMUPATCH(D3DDevice_SetBackBufferScale), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetDepthClipPlanes", xbox::EMUPATCH(D3DDevice_SetDepthClipPlanes), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetFlickerFilter", xbox::EMUPATCH(D3DDevice_SetFlickerFilter), PATCH_HLE_D3D),
@ -145,7 +138,7 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
PATCH_ENTRY("D3DDevice_SetPalette_4", xbox::EMUPATCH(D3DDevice_SetPalette_4), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetPixelShader", xbox::EMUPATCH(D3DDevice_SetPixelShader), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_SetPixelShaderConstant_4", xbox::EMUPATCH(D3DDevice_SetPixelShaderConstant_4), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetPixelShader_0__LTCG_eax_handle", xbox::EMUPATCH(D3DDevice_SetPixelShader_0__LTCG_eax_handle), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetPixelShader_0", xbox::EMUPATCH(D3DDevice_SetPixelShader_0), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetRenderState_Simple", xbox::EMUPATCH(D3DDevice_SetRenderState_Simple), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetRenderTarget", xbox::EMUPATCH(D3DDevice_SetRenderTarget), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetRenderTargetFast", xbox::EMUPATCH(D3DDevice_SetRenderTargetFast), PATCH_HLE_D3D),
@ -163,7 +156,7 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
PATCH_ENTRY("D3DDevice_SetSwapCallback", xbox::EMUPATCH(D3DDevice_SetSwapCallback), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetTexture", xbox::EMUPATCH(D3DDevice_SetTexture), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetTexture_4__LTCG_eax_pTexture", xbox::EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_pTexture), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetTexture_4__LTCG_eax_Stage", xbox::EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_Stage), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetTexture_4", xbox::EMUPATCH(D3DDevice_SetTexture_4), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetTransform", xbox::EMUPATCH(D3DDevice_SetTransform), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetTransform_0__LTCG_eax1_edx2", xbox::EMUPATCH(D3DDevice_SetTransform_0__LTCG_eax1_edx2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexData2f", xbox::EMUPATCH(D3DDevice_SetVertexData2f), PATCH_HLE_D3D),
@ -184,7 +177,7 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
PATCH_ENTRY("D3DDevice_SetVertexShaderConstant_8", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstant_8), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexShaderInput", xbox::EMUPATCH(D3DDevice_SetVertexShaderInput), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_SetVertexShaderInputDirect", xbox::EMUPATCH(D3DDevice_SetVertexShaderInputDirect), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_SetVerticalBlankCallback", xbox::EMUPATCH(D3DDevice_SetVerticalBlankCallback), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVerticalBlankCallback", xbox::EMUPATCH(D3DDevice_SetVerticalBlankCallback), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetViewport", xbox::EMUPATCH(D3DDevice_SetViewport), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_Swap", xbox::EMUPATCH(D3DDevice_Swap), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_Swap_0", xbox::EMUPATCH(D3DDevice_Swap_0), PATCH_HLE_D3D),
@ -376,54 +369,6 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
//PATCH_ENTRY("timeSetEvent", xbox::EMUPATCH(timeSetEvent), PATCH_ALWAYS),
PATCH_ENTRY("XReadMUMetaData", xbox::EMUPATCH(XReadMUMetaData), PATCH_ALWAYS),
PATCH_ENTRY("XUnmountMU", xbox::EMUPATCH(XUnmountMU), PATCH_ALWAYS),
// JVS Functions
PATCH_ENTRY("JVS_SendCommand", xbox::EMUPATCH(JVS_SendCommand), PATCH_ALWAYS),
PATCH_ENTRY("JVS_SendCommand2", xbox::EMUPATCH(JVS_SendCommand), PATCH_ALWAYS),
PATCH_ENTRY("JVS_SendCommand3", xbox::EMUPATCH(JVS_SendCommand), PATCH_ALWAYS),
PATCH_ENTRY("JvsBACKUP_Read", xbox::EMUPATCH(JvsBACKUP_Read), PATCH_ALWAYS),
PATCH_ENTRY("JvsBACKUP_Read2", xbox::EMUPATCH(JvsBACKUP_Read), PATCH_ALWAYS),
PATCH_ENTRY("JvsBACKUP_Read3", xbox::EMUPATCH(JvsBACKUP_Read), PATCH_ALWAYS),
PATCH_ENTRY("JvsBACKUP_Write", xbox::EMUPATCH(JvsBACKUP_Write), PATCH_ALWAYS),
PATCH_ENTRY("JvsBACKUP_Write2", xbox::EMUPATCH(JvsBACKUP_Write), PATCH_ALWAYS),
PATCH_ENTRY("JvsEEPROM_Read", xbox::EMUPATCH(JvsEEPROM_Read), PATCH_ALWAYS),
PATCH_ENTRY("JvsEEPROM_Read2", xbox::EMUPATCH(JvsEEPROM_Read), PATCH_ALWAYS),
PATCH_ENTRY("JvsEEPROM_Read3", xbox::EMUPATCH(JvsEEPROM_Read), PATCH_ALWAYS),
PATCH_ENTRY("JvsEEPROM_Write", xbox::EMUPATCH(JvsEEPROM_Write), PATCH_ALWAYS),
PATCH_ENTRY("JvsEEPROM_Write2", xbox::EMUPATCH(JvsEEPROM_Write), PATCH_ALWAYS),
PATCH_ENTRY("JvsEEPROM_Write3", xbox::EMUPATCH(JvsEEPROM_Write), PATCH_ALWAYS),
PATCH_ENTRY("JvsFirmwareDownload", xbox::EMUPATCH(JvsFirmwareDownload), PATCH_ALWAYS),
PATCH_ENTRY("JvsFirmwareDownload2", xbox::EMUPATCH(JvsFirmwareDownload), PATCH_ALWAYS),
PATCH_ENTRY("JvsFirmwareDownload3", xbox::EMUPATCH(JvsFirmwareDownload), PATCH_ALWAYS),
PATCH_ENTRY("JvsFirmwareDownload4", xbox::EMUPATCH(JvsFirmwareDownload), PATCH_ALWAYS),
PATCH_ENTRY("JvsFirmwareUpload", xbox::EMUPATCH(JvsFirmwareUpload), PATCH_ALWAYS),
PATCH_ENTRY("JvsFirmwareUpload2", xbox::EMUPATCH(JvsFirmwareUpload), PATCH_ALWAYS),
PATCH_ENTRY("JvsFirmwareUpload3", xbox::EMUPATCH(JvsFirmwareUpload), PATCH_ALWAYS),
PATCH_ENTRY("JvsFirmwareUpload4", xbox::EMUPATCH(JvsFirmwareUpload), PATCH_ALWAYS),
PATCH_ENTRY("JvsNodeReceivePacket", xbox::EMUPATCH(JvsNodeReceivePacket), PATCH_ALWAYS),
PATCH_ENTRY("JvsNodeReceivePacket2", xbox::EMUPATCH(JvsNodeReceivePacket), PATCH_ALWAYS),
PATCH_ENTRY("JvsNodeSendPacket", xbox::EMUPATCH(JvsNodeSendPacket), PATCH_ALWAYS),
PATCH_ENTRY("JvsNodeSendPacket2", xbox::EMUPATCH(JvsNodeSendPacket), PATCH_ALWAYS),
PATCH_ENTRY("JvsRTC_Read", xbox::EMUPATCH(JvsRTC_Read), PATCH_ALWAYS),
PATCH_ENTRY("JvsRTC_Read2", xbox::EMUPATCH(JvsRTC_Read), PATCH_ALWAYS),
PATCH_ENTRY("JvsRTC_Read3", xbox::EMUPATCH(JvsRTC_Read), PATCH_ALWAYS),
PATCH_ENTRY("JvsRTC_Write", xbox::EMUPATCH(JvsRTC_Write), PATCH_ALWAYS),
PATCH_ENTRY("JvsRTC_Write2", xbox::EMUPATCH(JvsRTC_Write), PATCH_ALWAYS),
PATCH_ENTRY("JvsScFirmwareDownload", xbox::EMUPATCH(JvsScFirmwareDownload), PATCH_ALWAYS),
PATCH_ENTRY("JvsScFirmwareDownload2", xbox::EMUPATCH(JvsScFirmwareDownload), PATCH_ALWAYS),
PATCH_ENTRY("JvsScFirmwareDownload3", xbox::EMUPATCH(JvsScFirmwareDownload), PATCH_ALWAYS),
PATCH_ENTRY("JvsScFirmwareDownload4", xbox::EMUPATCH(JvsScFirmwareDownload), PATCH_ALWAYS),
PATCH_ENTRY("JvsScFirmwareUpload", xbox::EMUPATCH(JvsScFirmwareUpload), PATCH_ALWAYS),
PATCH_ENTRY("JvsScFirmwareUpload2", xbox::EMUPATCH(JvsScFirmwareUpload), PATCH_ALWAYS),
PATCH_ENTRY("JvsScFirmwareUpload3", xbox::EMUPATCH(JvsScFirmwareUpload), PATCH_ALWAYS),
PATCH_ENTRY("JvsScReceiveMidi", xbox::EMUPATCH(JvsScReceiveMidi), PATCH_ALWAYS),
PATCH_ENTRY("JvsScReceiveMidi2", xbox::EMUPATCH(JvsScReceiveMidi), PATCH_ALWAYS),
PATCH_ENTRY("JvsScReceiveRs323c", xbox::EMUPATCH(JvsScReceiveRs323c), PATCH_ALWAYS),
PATCH_ENTRY("JvsScReceiveRs323c2", xbox::EMUPATCH(JvsScReceiveRs323c), PATCH_ALWAYS),
PATCH_ENTRY("JvsScSendMidi", xbox::EMUPATCH(JvsScSendMidi), PATCH_ALWAYS),
PATCH_ENTRY("JvsScSendMidi2", xbox::EMUPATCH(JvsScSendMidi), PATCH_ALWAYS),
PATCH_ENTRY("JvsScSendRs323c", xbox::EMUPATCH(JvsScSendRs323c), PATCH_ALWAYS),
PATCH_ENTRY("JvsScSendRs323c2", xbox::EMUPATCH(JvsScSendRs323c), PATCH_ALWAYS),
};
std::unordered_map<std::string, subhook::Hook> g_FunctionHooks;

View file

@ -399,34 +399,91 @@ void DestructHleInputDevice(DeviceState *dev)
void SetupXboxDeviceTypes()
{
// Get address to xpp type's devices
if (xbox::addr_xt gamepad_xpp_type = g_SymbolAddresses["g_DeviceType_Gamepad"]) {
g_DeviceType_Gamepad = reinterpret_cast<xbox::PXPP_DEVICE_TYPE>(gamepad_xpp_type);
}
#if 0 // Not implemented
if (xbox::addr_xt ir_dongle_xpp_type = g_SymbolAddresses["g_DeviceType_IRDongle"]) {
g_DeviceType_IRDongle = reinterpret_cast<xbox::PXPP_DEVICE_TYPE>(ir_dongle_xpp_type);
}
if (xbox::addr_xt keyboard_xpp_type = g_SymbolAddresses["g_DeviceType_Keyboard"]) {
g_DeviceType_Keyboard = reinterpret_cast<xbox::PXPP_DEVICE_TYPE>(keyboard_xpp_type);
}
if (xbox::addr_xt mouse_xpp_type = g_SymbolAddresses["g_DeviceType_Mouse"]) {
g_DeviceType_Mouse = reinterpret_cast<xbox::PXPP_DEVICE_TYPE>(mouse_xpp_type);
}
#endif
if (xbox::addr_xt sbc_xpp_type = g_SymbolAddresses["g_DeviceType_SBC"]) {
g_DeviceType_SBC = reinterpret_cast<xbox::PXPP_DEVICE_TYPE>(sbc_xpp_type);
}
if (xbox::addr_xt mu_xpp_type = g_SymbolAddresses["g_DeviceType_MU"]) {
g_DeviceType_MU = reinterpret_cast<xbox::PXPP_DEVICE_TYPE>(mu_xpp_type);
// If we don't yet have the offset to gDeviceType_Gamepad, work it out!
if (g_DeviceType_Gamepad == nullptr) {
// First, attempt to find GetTypeInformation
auto typeInformation = g_SymbolAddresses.find("GetTypeInformation");
if (typeInformation != g_SymbolAddresses.end() && typeInformation->second != xbox::zero) {
EmuLog(LOG_LEVEL::INFO, "Deriving XDEVICE_TYPE_GAMEPAD from DeviceTable (via GetTypeInformation)");
// Read the offset values of the device table structure from GetTypeInformation
xbox::addr_xt deviceTableStartOffset = *(uint32_t*)((uint32_t)typeInformation->second + 0x01);
xbox::addr_xt deviceTableEndOffset = *(uint32_t*)((uint32_t)typeInformation->second + 0x09);
// Calculate the number of device entires in the table
size_t deviceTableEntryCount = (deviceTableEndOffset - deviceTableStartOffset) / sizeof(uint32_t);
EmuLog(LOG_LEVEL::INFO, "DeviceTableStart: 0x%08X", deviceTableStartOffset);
EmuLog(LOG_LEVEL::INFO, "DeviceTableEnd: 0x%08X", deviceTableEndOffset);
EmuLog(LOG_LEVEL::INFO, "DeviceTable Entires: %u", deviceTableEntryCount);
// Sanity check: Where all these device offsets within Xbox memory
if ((deviceTableStartOffset >= g_SystemMaxMemory) || (deviceTableEndOffset >= g_SystemMaxMemory)) {
CxbxrAbort("DeviceTable Location is outside of Xbox Memory range");
}
// Iterate through the table until we find gamepad
xbox::PXID_TYPE_INFORMATION* deviceTable = (xbox::PXID_TYPE_INFORMATION*)(deviceTableStartOffset);
for (unsigned int i = 0; i < deviceTableEntryCount; i++) {
// Skip empty table entries
if (deviceTable[i] == nullptr) {
continue;
}
EmuLog(LOG_LEVEL::INFO, "----------------------------------------");
EmuLog(LOG_LEVEL::INFO, "DeviceTable[%u]->ucType = %d", i, deviceTable[i]->ucType);
switch (deviceTable[i]->ucType) {
case XINPUT_DEVTYPE_GAMEPAD:
g_DeviceType_Gamepad = deviceTable[i]->XppType;
EmuLog(LOG_LEVEL::INFO, "DeviceTable[%u]->XppType = 0x%08X (XDEVICE_TYPE_GAMEPAD)", i, (uintptr_t)g_DeviceType_Gamepad);
break;
case XINPUT_DEVTYPE_STEELBATTALION:
g_DeviceType_SBC = deviceTable[i]->XppType;
EmuLog(LOG_LEVEL::INFO, "DeviceTable[%u]->XppType = 0x%08X (XDEVICE_TYPE_STEELBATTALION)", i, (uintptr_t)g_DeviceType_SBC);
break;
default:
EmuLog(LOG_LEVEL::WARNING, "DeviceTable[%u]->XppType = 0x%08X (Unknown device type)", i, (uintptr_t)deviceTable[i]->XppType);
continue;
}
}
} else {
// XDKs without GetTypeInformation have the GamePad address hardcoded in XInputOpen
// Only the earliest XDKs use this code path, and the offset never changed between them
// so this works well for us.
void* XInputOpenAddr = (void*)g_SymbolAddresses["XInputOpen"];
if (XInputOpenAddr != nullptr) {
EmuLog(LOG_LEVEL::INFO, "Deriving XDEVICE_TYPE_GAMEPAD from XInputOpen (0x%08X)", (uintptr_t)XInputOpenAddr);
g_DeviceType_Gamepad = *(xbox::PXPP_DEVICE_TYPE*)((uint32_t)XInputOpenAddr + 0x0B);
}
}
if (g_DeviceType_Gamepad == nullptr) {
EmuLog(LOG_LEVEL::WARNING, "XDEVICE_TYPE_GAMEPAD was not found");
return;
}
EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_GAMEPAD found at 0x%08X", (uintptr_t)g_DeviceType_Gamepad);
}
// Get additional variables relative to Memory Unit
if (xbox::addr_xt xapi_mounted_mu = g_SymbolAddresses["g_XapiMountedMUs"]) {
g_XapiMountedMUs = reinterpret_cast<xbox::ulong_xt*>(xapi_mounted_mu);
if (xbox::addr_xt mu_xpp_type = g_SymbolAddresses["g_DeviceType_MU"]) {
g_DeviceType_MU = reinterpret_cast<xbox::PXPP_DEVICE_TYPE>(mu_xpp_type);
EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_MEMORY_UNIT found at 0x%08X", reinterpret_cast<uintptr_t>(g_DeviceType_MU));
}
if (xbox::addr_xt xapi_alt_lett_mu = g_SymbolAddresses["g_XapiAltLett_MU"]) {
g_XapiAltLett_MU = reinterpret_cast<xbox::char_xt *>(xapi_alt_lett_mu);
else {
EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_MEMORY_UNIT was not found by XbSymbolDatabase");
}
if (xbox::addr_xt xapi_mounted_mu = g_SymbolAddresses["g_XapiMountedMUs"]) {
g_XapiMountedMUs = reinterpret_cast<xbox::ulong_xt *>(xapi_mounted_mu);
EmuLog(LOG_LEVEL::INFO, "XapiMountedMUs found at 0x%08X", reinterpret_cast<uintptr_t>(g_XapiMountedMUs));
g_XapiAltLett_MU = reinterpret_cast<xbox::char_xt *>(g_XapiMountedMUs - 1);
EmuLog(LOG_LEVEL::INFO, "XapiAltLett_MU found at 0x%08X", reinterpret_cast<uintptr_t>(g_XapiAltLett_MU));
}
else {
EmuLog(LOG_LEVEL::INFO, "XapiMountedMUs was not found by XbSymbolDatabase");
}
}
@ -960,41 +1017,15 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait)
NewTime.QuadPart += (static_cast<xbox::ulonglong_xt>(dwMilliseconds) * CLOCK_TIME_INCREMENT);
}
PKTHREAD kThread = KeGetCurrentThread();
kThread->WaitStatus = X_STATUS_SUCCESS;
if (!AddWaitObject(kThread, Timeout)) {
RETURN(WAIT_TIMEOUT);
}
xbox::ntstatus_xt status = WaitApc<true>([hObjectToSignal, hObjectToWaitOn, bAlertable](xbox::PKTHREAD kThread) -> std::optional<DWORD> {
xbox::dword_xt ret = WaitApc([hObjectToSignal, hObjectToWaitOn, bAlertable]() -> std::optional<DWORD> {
DWORD dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, 0, bAlertable);
if (dwRet == WAIT_TIMEOUT) {
return std::nullopt;
}
// If the wait was satisfied with the host, then also unwait the thread on the guest side, to be sure to remove WaitBlocks that might have been added
// to the thread
xbox::ntstatus_xt Status;
switch (dwRet)
{
case WAIT_ABANDONED: Status = X_STATUS_ABANDONED; break;
case WAIT_IO_COMPLETION: Status = X_STATUS_USER_APC; break;
case WAIT_OBJECT_0: Status = X_STATUS_SUCCESS; break;
default: Status = X_STATUS_INVALID_HANDLE;
}
xbox::KiUnwaitThreadAndLock(kThread, Status, 0);
return std::make_optional<ntstatus_xt>(kThread->WaitStatus);
}, Timeout, bAlertable, UserMode, kThread);
return std::make_optional<DWORD>(dwRet);
}, Timeout, bAlertable, UserMode);
xbox::dword_xt ret;
switch (status)
{
case X_STATUS_ABANDONED: ret = WAIT_ABANDONED; break;
case X_STATUS_USER_APC: ret = WAIT_IO_COMPLETION; break;
case X_STATUS_SUCCESS: ret = WAIT_OBJECT_0; break;
case X_STATUS_TIMEOUT: ret = WAIT_TIMEOUT; break;
default: ret = WAIT_FAILED;
}
RETURN(ret);
RETURN((ret == X_STATUS_USER_APC) ? WAIT_IO_COMPLETION : (ret == X_STATUS_TIMEOUT) ? WAIT_TIMEOUT : ret);
}
// ******************************************************************

View file

@ -605,9 +605,6 @@ XBSYSAPI EXPORTNUM(352) void_xt NTAPI RtlRip
PCHAR Message
);
void_xt RtlInitSystem();
extern RTL_CRITICAL_SECTION NtSystemTimeCritSec;
}
#endif

View file

@ -98,8 +98,6 @@ typedef void* LPSECURITY_ATTRIBUTES;
#define X_STATUS_FILE_IS_A_DIRECTORY 0xC00000BAL
#define X_STATUS_END_OF_FILE 0xC0000011L
#define X_STATUS_INVALID_PAGE_PROTECTION 0xC0000045L
#define X_STATUS_SUSPEND_COUNT_EXCEEDED 0xC000004AL
#define X_STATUS_THREAD_IS_TERMINATING 0xC000004BL
#define X_STATUS_CONFLICTING_ADDRESSES 0xC0000018L
#define X_STATUS_UNABLE_TO_FREE_VM 0xC000001AL
#define X_STATUS_FREE_VM_NOT_AT_BASE 0xC000009FL
@ -268,7 +266,7 @@ typedef struct _UNICODE_STRING
{
ushort_xt Length;
ushort_xt MaximumLength;
wchar_xt *Buffer;
ushort_xt *Buffer;
}
UNICODE_STRING, *PUNICODE_STRING;
@ -1947,7 +1945,7 @@ typedef struct _KTHREAD
/* 0x56/86 */ char_xt WaitNext;
/* 0x57/87 */ char_xt WaitReason;
/* 0x58/88 */ PKWAIT_BLOCK WaitBlockList;
/* 0x5C/92 */ LIST_ENTRY WaitListEntry; // Used to place the thread in the ready list of the scheduler
/* 0x5C/92 */ LIST_ENTRY WaitListEntry;
/* 0x64/100 */ ulong_xt WaitTime;
/* 0x68/104 */ ulong_xt KernelApcDisable;
/* 0x6C/108 */ ulong_xt Quantum;
@ -1971,8 +1969,6 @@ typedef struct _KTHREAD
}
KTHREAD, *PKTHREAD, *RESTRICTED_POINTER PRKTHREAD;
#define X_MAXIMUM_SUSPEND_COUNT 0x7F
// ******************************************************************
// * ETHREAD
// ******************************************************************
@ -2085,11 +2081,6 @@ typedef enum _XC_VALUE_INDEX
}
XC_VALUE_INDEX, *PXC_VALUE_INDEX;
#define XBOX_HW_FLAG_INTERNAL_USB_HUB 0x00000001
#define XBOX_HW_FLAG_DEVKIT_KERNEL 0x00000002
#define XBOX_480P_MACROVISION_ENABLED 0x00000004
#define XBOX_HW_FLAG_ARCADE 0x00000008
// ******************************************************************
// * XBOX_HARDWARE_INFO
// ******************************************************************

View file

@ -85,8 +85,6 @@ void InsertTailList(xbox::PLIST_ENTRY pListHead, xbox::PLIST_ENTRY pEntry)
//#define RemoveEntryList(e) do { PLIST_ENTRY f = (e)->Flink, b = (e)->Blink; f->Blink = b; b->Flink = f; (e)->Flink = (e)->Blink = NULL; } while (0)
// Returns TRUE if the list has become empty after removing the element, FALSE otherwise.
// NOTE: this function is a mess. _EX_Flink and _EX_Flink should never be nullptr, and it should never be called on a detached element either. Try to fix
// the bugs in the caller instead of trying to handle it here with these hacks
xbox::boolean_xt RemoveEntryList(xbox::PLIST_ENTRY pEntry)
{
xbox::PLIST_ENTRY _EX_Flink = pEntry->Flink;
@ -142,6 +140,8 @@ void RestoreInterruptMode(bool value)
g_bInterruptsEnabled = value;
}
extern void ExecuteDpcQueue();
void KiUnexpectedInterrupt()
{
xbox::KeBugCheck(TRAP_CAUSE_UNKNOWN); // see
@ -158,10 +158,7 @@ void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql)
xbox::KiExecuteKernelApc();
break;
case DISPATCH_LEVEL: // = 2
// This can be recursively called by KiUnlockDispatcherDatabase and KfLowerIrql, so avoid calling DPCs again if the current one has queued yet another one
if (!IsDpcActive()) { // Avoid KeIsExecutingDpc(), as that logs
ExecuteDpcQueue();
}
ExecuteDpcQueue();
break;
case APC_LEVEL | DISPATCH_LEVEL: // = 3
KiUnexpectedInterrupt();
@ -178,33 +175,6 @@ void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql)
HalInterruptRequestRegister ^= (1 << SoftwareIrql);
}
bool AddWaitObject(xbox::PKTHREAD kThread, xbox::PLARGE_INTEGER Timeout)
{
// Use the built-in ktimer as a dummy wait object, so that KiUnwaitThreadAndLock can still work
xbox::KiTimerLock();
xbox::PKWAIT_BLOCK WaitBlock = &kThread->TimerWaitBlock;
kThread->WaitBlockList = WaitBlock;
xbox::PKTIMER Timer = &kThread->Timer;
WaitBlock->NextWaitBlock = WaitBlock;
Timer->Header.WaitListHead.Flink = &WaitBlock->WaitListEntry;
Timer->Header.WaitListHead.Blink = &WaitBlock->WaitListEntry;
if (Timeout && Timeout->QuadPart) {
// Setup a timer so that KiTimerExpiration can discover the timeout and yield to us.
// Otherwise, we will only be able to discover the timeout when Windows decides to schedule us again, and testing shows that
// tends to happen much later than the due time
if (xbox::KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
// Sanity check: set WaitBlockList to nullptr so that we can catch the case where a waiter starts a new wait but forgets to setup a new wait block. This
// way, we will crash instead of silently using the pointer to the old block
kThread->WaitBlockList = xbox::zeroptr;
xbox::KiTimerUnlock();
return false;
}
}
kThread->State = xbox::Waiting;
xbox::KiTimerUnlock();
return true;
}
// This masks have been verified to be correct against a kernel dump
const DWORD IrqlMasks[] = {
0xFFFFFFFE, // IRQL 0
@ -480,8 +450,7 @@ XBSYSAPI EXPORTNUM(163) xbox::void_xt FASTCALL xbox::KiUnlockDispatcherDatabase
LOG_FUNC_ONE_ARG_TYPE(KIRQL_TYPE, OldIrql);
// Wrong, this should only happen when OldIrql >= DISPATCH_LEVEL
// Checking DpcRoutineActive doesn't work because our Prcb is per-thread instead of being per-processor
if (!IsDpcActive()) { // Avoid KeIsExecutingDpc(), as that logs
if (!(KeGetCurrentPrcb()->DpcRoutineActive)) { // Avoid KeIsExecutingDpc(), as that logs
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
}

View file

@ -52,8 +52,8 @@ xbox::PLIST_ENTRY RemoveTailList(xbox::PLIST_ENTRY pListHead);
extern xbox::LAUNCH_DATA_PAGE DefaultLaunchDataPage;
extern xbox::PKINTERRUPT EmuInterruptList[MAX_BUS_INTERRUPT_LEVEL + 1];
// Indicates to disable/enable all interrupts when cli and sti instructions are executed
inline std::atomic_bool g_bEnableAllInterrupts = true;
inline std::condition_variable g_InterruptSignal;
inline std::atomic_bool g_AnyInterruptAsserted = false;
class HalSystemInterrupt {
public:
@ -64,6 +64,8 @@ public:
}
m_Asserted = state;
g_AnyInterruptAsserted = true;
g_InterruptSignal.notify_one();
};
void Enable() {
@ -108,18 +110,17 @@ extern HalSystemInterrupt HalSystemInterrupts[MAX_BUS_INTERRUPT_LEVEL + 1];
bool DisableInterrupts();
void RestoreInterruptMode(bool value);
void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql);
bool AddWaitObject(xbox::PKTHREAD kThread, xbox::PLARGE_INTEGER Timeout);
template<typename T>
std::optional<xbox::ntstatus_xt> SatisfyWait(T &&Lambda, xbox::PKTHREAD kThread, xbox::boolean_xt Alertable, xbox::char_xt WaitMode)
std::optional<xbox::ntstatus_xt> SatisfyWait(T &&Lambda, xbox::PETHREAD eThread, xbox::boolean_xt Alertable, xbox::char_xt WaitMode)
{
if (const auto ret = Lambda(kThread)) {
if (const auto ret = Lambda()) {
return ret;
}
xbox::KiApcListMtx.lock();
bool EmptyKernel = IsListEmpty(&kThread->ApcState.ApcListHead[xbox::KernelMode]);
bool EmptyUser = IsListEmpty(&kThread->ApcState.ApcListHead[xbox::UserMode]);
bool EmptyKernel = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[xbox::KernelMode]);
bool EmptyUser = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[xbox::UserMode]);
xbox::KiApcListMtx.unlock();
if (EmptyKernel == false) {
@ -130,65 +131,56 @@ std::optional<xbox::ntstatus_xt> SatisfyWait(T &&Lambda, xbox::PKTHREAD kThread,
(Alertable == TRUE) &&
(WaitMode == xbox::UserMode)) {
xbox::KiExecuteUserApc();
xbox::KiUnwaitThreadAndLock(kThread, X_STATUS_USER_APC, 0);
return kThread->WaitStatus;
return X_STATUS_USER_APC;
}
return std::nullopt;
}
template<bool host_wait, typename T>
xbox::ntstatus_xt WaitApc(T &&Lambda, xbox::PLARGE_INTEGER Timeout, xbox::boolean_xt Alertable, xbox::char_xt WaitMode, xbox::PKTHREAD kThread)
template<typename T>
xbox::ntstatus_xt WaitApc(T &&Lambda, xbox::PLARGE_INTEGER Timeout, xbox::boolean_xt Alertable, xbox::char_xt WaitMode)
{
// NOTE1: kThread->Alerted is currently never set. When the alerted mechanism is implemented, the alerts should
// also interrupt the wait.
// NOTE: kThread->Alerted is currently never set. When the alerted mechanism is implemented, the alerts should
// also interrupt the wait
xbox::PETHREAD eThread = reinterpret_cast<xbox::PETHREAD>(EmuKeGetPcr()->Prcb->CurrentThread);
xbox::ntstatus_xt status;
if (Timeout == nullptr) {
// No timout specified, so this is an infinite wait until an alert, a user apc or the object(s) become(s) signalled
while (true) {
if (const auto ret = SatisfyWait(Lambda, kThread, Alertable, WaitMode)) {
status = *ret;
break;
if (const auto ret = SatisfyWait(Lambda, eThread, Alertable, WaitMode)) {
return *ret;
}
std::this_thread::yield();
}
}
else if (Timeout->QuadPart == 0) {
assert(host_wait);
// A zero timeout means that we only have to check the conditions once and then return immediately if they are not satisfied
if (const auto ret = SatisfyWait(Lambda, kThread, Alertable, WaitMode)) {
status = *ret;
if (const auto ret = SatisfyWait(Lambda, eThread, Alertable, WaitMode)) {
return *ret;
}
else {
// If the wait failed, then always remove the wait block. Note that this can only happen with host waits, since guest waits never call us at all
// when Timeout->QuadPart == 0. Test case: Halo 2 (sporadically when playing the intro video)
xbox::KiUnwaitThreadAndLock(kThread, X_STATUS_TIMEOUT, 0);
status = kThread->WaitStatus;
return X_STATUS_TIMEOUT;
}
}
else {
// A non-zero timeout means we have to check the conditions until we reach the requested time
while (true) {
if (const auto ret = SatisfyWait(Lambda, kThread, Alertable, WaitMode)) {
status = *ret;
break;
}
if (host_wait && (kThread->State == xbox::Ready)) {
status = kThread->WaitStatus;
break;
xbox::LARGE_INTEGER ExpireTime, DueTime, NewTime;
xbox::ulonglong_xt Now;
ExpireTime.QuadPart = DueTime.QuadPart = Timeout->QuadPart; // either positive, negative, but not NULL
xbox::PLARGE_INTEGER AbsoluteExpireTime = xbox::KiComputeWaitInterval(&ExpireTime, &DueTime, &NewTime, &Now);
while (Now <= static_cast<xbox::ulonglong_xt>(AbsoluteExpireTime->QuadPart)) {
if (const auto ret = SatisfyWait(Lambda, eThread, Alertable, WaitMode)) {
return *ret;
}
std::this_thread::yield();
Now = xbox::KeQueryInterruptTime();
}
}
if constexpr (host_wait) {
kThread->State = xbox::Running;
return X_STATUS_TIMEOUT;
}
return status;
}
#endif

View file

@ -43,6 +43,7 @@
#include "common\EmuEEPROM.h" // For EEPROM
#include "devices\Xbox.h" // For g_SMBus, SMBUS_ADDRESS_SYSTEM_MICRO_CONTROLLER
#include "devices\SMCDevice.h" // For SMC_COMMAND_SCRATCH
#include "common/util/strConverter.hpp" // for utf16_to_ascii
#include "core\kernel\memory-manager\VMManager.h"
#include <algorithm> // for std::replace
@ -582,7 +583,6 @@ XBSYSAPI EXPORTNUM(49) xbox::void_xt DECLSPEC_NORETURN NTAPI xbox::HalReturnToFi
case ReturnFirmwareFatal:
{
xbox::HalWriteSMBusValue(SMBUS_ADDRESS_SYSTEM_MICRO_CONTROLLER, SMC_COMMAND_SCRATCH, 0, SMC_SCRATCH_DISPLAY_FATAL_ERROR);
is_reboot = true;
g_VMManager.SavePersistentMemory();

View file

@ -248,14 +248,6 @@ XBSYSAPI EXPORTNUM(66) xbox::ntstatus_xt NTAPI xbox::IoCreateFile
LOG_FUNC_ARG(Options)
LOG_FUNC_END;
// If we are emulating the Chihiro, we need to hook mbcom, so return an easily identifable handle
if (g_bIsChihiro) {
if (strncmp(ObjectAttributes->ObjectName->Buffer, DriveMbcom.c_str(), DriveMbcom.length()) == 0) {
*FileHandle = CHIHIRO_MBCOM_HANDLE;
return X_STATUS_SUCCESS;
}
}
NativeObjectAttributes nativeObjectAttributes;
// If we are NOT accessing a directory, and we match a partition path, we need to redirect to a partition.bin file
@ -281,17 +273,6 @@ XBSYSAPI EXPORTNUM(66) xbox::ntstatus_xt NTAPI xbox::IoCreateFile
// Force ShareAccess to all
ShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
// Force set DELETE permission flag if write attributes flag is set.
// Testcase: dashupdate.xbe (4928, untested with other versions but newer dashupdate did not call for deletion on fail).
if (DesiredAccess & FILE_WRITE_ATTRIBUTES) {
DesiredAccess |= DELETE;
}
// Force sanitize before call to NtDll::NtCreateFile
// Testcase:
// * Exhibition Demo discs - Attempt to create music folder fail internally which then show unable to copy soundtrack dialog.
FileAttributes &= FILE_ATTRIBUTE_VALID_FLAGS;
if (SUCCEEDED(ret))
{
// redirect to NtCreateFile

View file

@ -96,13 +96,11 @@ namespace NtDll
// TODO : Move towards thread-simulation based Dpc emulation
typedef struct _DpcData {
CRITICAL_SECTION Lock;
std::atomic_flag IsDpcActive;
std::atomic_flag IsDpcPending;
HANDLE DpcEvent;
xbox::LIST_ENTRY DpcQueue; // TODO : Use KeGetCurrentPrcb()->DpcListHead instead
} DpcData;
DpcData g_DpcData = { 0 }; // Note : g_DpcData is initialized in InitDpcData()
std::atomic_flag xbox::KeSystemTimeChanged;
DpcData g_DpcData = { 0 }; // Note : g_DpcData is initialized in InitDpcThread()
xbox::ulonglong_xt LARGE_INTEGER2ULONGLONG(xbox::LARGE_INTEGER value)
{
@ -132,33 +130,6 @@ xbox::ulonglong_xt LARGE_INTEGER2ULONGLONG(xbox::LARGE_INTEGER value)
break; \
}
xbox::void_xt xbox::KeResumeThreadEx
(
IN PKTHREAD Thread
)
{
// This is only to be used to synchronize new thread creation with the thread that spawned it
Thread->SuspendSemaphore.Header.SignalState = 1;
KiWaitListLock();
KiWaitTest(&Thread->SuspendSemaphore, 0);
}
xbox::void_xt xbox::KeSuspendThreadEx
(
IN PKTHREAD Thread
)
{
// This is only to be used to synchronize new thread creation with the thread that spawned it
Thread->SuspendSemaphore.Header.SignalState = 0;
KiInsertQueueApc(&Thread->SuspendApc, 0);
}
xbox::void_xt xbox::KeWaitForDpc()
{
g_DpcData.IsDpcPending.wait(false);
}
// ******************************************************************
// * EmuKeGetPcr()
@ -195,7 +166,7 @@ xbox::void_xt NTAPI xbox::KeSetSystemTime
)
{
KIRQL OldIrql, OldIrql2;
LARGE_INTEGER DeltaTime;
LARGE_INTEGER DeltaTime, HostTime;
PLIST_ENTRY ListHead, NextEntry;
PKTIMER Timer;
LIST_ENTRY TempList, TempList2;
@ -213,6 +184,10 @@ xbox::void_xt NTAPI xbox::KeSetSystemTime
/* Query the system time now */
KeQuerySystemTime(OldTime);
/* Surely, we won't set the system time here, but we CAN remember a delta to the host system time */
HostTime.QuadPart = OldTime->QuadPart - HostSystemTimeDelta.load();
HostSystemTimeDelta = NewTime->QuadPart - HostTime.QuadPart;
/* Calculate the difference between the new and the old time */
DeltaTime.QuadPart = NewTime->QuadPart - OldTime->QuadPart;
@ -271,7 +246,7 @@ xbox::void_xt NTAPI xbox::KeSetSystemTime
}
}
/* Process expired timers. This releases the dispatcher and timer locks, then it yields */
/* Process expired timers. This releases the dispatcher and timer locks */
KiTimerListExpire(&TempList2, OldIrql);
}
@ -485,11 +460,8 @@ void ExecuteDpcQueue()
// Mark it as no longer linked into the DpcQueue
pkdpc->Inserted = FALSE;
// Set DpcRoutineActive to support KeIsExecutingDpc:
g_DpcData.IsDpcActive.test_and_set();
KeGetCurrentPrcb()->DpcRoutineActive = TRUE; // Experimental
LeaveCriticalSection(&(g_DpcData.Lock));
EmuLog(LOG_LEVEL::DEBUG, "Global DpcQueue, calling DPC object 0x%.8X at 0x%.8X", pkdpc, pkdpc->DeferredRoutine);
EmuLog(LOG_LEVEL::DEBUG, "Global DpcQueue, calling DPC at 0x%.8X", pkdpc->DeferredRoutine);
// Call the Deferred Procedure :
pkdpc->DeferredRoutine(
@ -498,30 +470,23 @@ void ExecuteDpcQueue()
pkdpc->SystemArgument1,
pkdpc->SystemArgument2);
EnterCriticalSection(&(g_DpcData.Lock));
KeGetCurrentPrcb()->DpcRoutineActive = FALSE; // Experimental
g_DpcData.IsDpcActive.clear();
}
g_DpcData.IsDpcPending.clear();
// Assert(g_DpcData._dwThreadId == GetCurrentThreadId());
// Assert(g_DpcData._dwDpcThreadId == g_DpcData._dwThreadId);
// g_DpcData._dwDpcThreadId = 0;
LeaveCriticalSection(&(g_DpcData.Lock));
}
void InitDpcData()
void InitDpcThread()
{
// Let's initialize the Dpc handling thread too,
// here for now (should be called by our caller)
DWORD dwThreadId = 0;
InitializeCriticalSection(&(g_DpcData.Lock));
InitializeListHead(&(g_DpcData.DpcQueue));
}
bool IsDpcActive()
{
return g_DpcData.IsDpcActive.test();
EmuLogEx(CXBXR_MODULE::INIT, LOG_LEVEL::DEBUG, "Creating DPC event\n");
g_DpcData.DpcEvent = CreateEvent(/*lpEventAttributes=*/nullptr, /*bManualReset=*/FALSE, /*bInitialState=*/FALSE, /*lpName=*/nullptr);
}
static constexpr uint32_t XBOX_TSC_FREQUENCY = 733333333; // Xbox Time Stamp Counter Frequency = 733333333 (CPU Clock)
@ -533,6 +498,13 @@ ULONGLONG CxbxGetPerformanceCounter(bool acpi)
return Timer_GetScaledPerformanceCounter(period);
}
void CxbxInitPerformanceCounters()
{
// Let's initialize the Dpc handling thread too,
// here for now (should be called by our caller)
InitDpcThread();
}
// ******************************************************************
// * 0x005C - KeAlertResumeThread()
// ******************************************************************
@ -743,13 +715,7 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread
// We can't remove NtDll::NtDelayExecution until all APCs queued by Io are implemented by our kernel as well
// Test case: Metal Slug 3
PKTHREAD kThread = KeGetCurrentThread();
kThread->WaitStatus = X_STATUS_SUCCESS;
if (!AddWaitObject(kThread, Interval)) {
RETURN(X_STATUS_TIMEOUT);
}
xbox::ntstatus_xt ret = WaitApc<true>([Alertable](xbox::PKTHREAD kThread) -> std::optional<ntstatus_xt> {
xbox::ntstatus_xt ret = WaitApc([Alertable]() -> std::optional<ntstatus_xt> {
NtDll::LARGE_INTEGER ExpireTime;
ExpireTime.QuadPart = 0;
NTSTATUS Status = NtDll::NtDelayExecution(Alertable, &ExpireTime);
@ -757,11 +723,8 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread
if (Status >= 0 && Status != STATUS_ALERTED && Status != STATUS_USER_APC) {
return std::nullopt;
}
// If the wait was satisfied with the host, then also unwait the thread on the guest side, to be sure to remove WaitBlocks that might have been added
// to the thread. Test case: Steel Battalion
xbox::KiUnwaitThreadAndLock(kThread, Status, 0);
return std::make_optional<ntstatus_xt>(kThread->WaitStatus);
}, Interval, Alertable, WaitMode, kThread);
return std::make_optional<ntstatus_xt>(Status);
}, Interval, Alertable, WaitMode);
if (ret == X_STATUS_TIMEOUT) {
// NOTE: this function considers a timeout a success
@ -1243,9 +1206,35 @@ XBSYSAPI EXPORTNUM(118) xbox::boolean_xt NTAPI xbox::KeInsertQueueApc
Apc->SystemArgument1 = SystemArgument1;
Apc->SystemArgument2 = SystemArgument2;
boolean_xt result = KiInsertQueueApc(Apc, Increment);
KfLowerIrql(OldIrql);
RETURN(result);
if (Apc->Inserted) {
KfLowerIrql(OldIrql);
RETURN(FALSE);
}
else {
KiApcListMtx.lock();
InsertTailList(&kThread->ApcState.ApcListHead[Apc->ApcMode], &Apc->ApcListEntry);
Apc->Inserted = TRUE;
KiApcListMtx.unlock();
// We can only attempt to execute the queued apc right away if it is been inserted in the current thread, because otherwise the KTHREAD
// in the fs selector will not be correct
if (kThread == KeGetCurrentThread()) {
if (Apc->ApcMode == KernelMode) { // kernel apc
// NOTE: this is wrong, we should check the thread state instead of just signaling the kernel apc, but we currently
// don't set the appropriate state in kthread
kThread->ApcState.KernelApcPending = TRUE;
KiExecuteKernelApc();
}
else if ((kThread->WaitMode == UserMode) && (kThread->Alertable)) { // user apc
// NOTE: this should also check the thread state
kThread->ApcState.UserApcPending = TRUE;
KiExecuteUserApc();
}
}
KfLowerIrql(OldIrql);
RETURN(TRUE);
}
}
}
@ -1277,25 +1266,16 @@ XBSYSAPI EXPORTNUM(119) xbox::boolean_xt NTAPI xbox::KeInsertQueueDpc
Dpc->SystemArgument1 = SystemArgument1;
Dpc->SystemArgument2 = SystemArgument2;
InsertTailList(&(g_DpcData.DpcQueue), &(Dpc->DpcListEntry));
LeaveCriticalSection(&(g_DpcData.Lock));
g_DpcData.IsDpcPending.test_and_set();
g_DpcData.IsDpcPending.notify_one();
// TODO : Instead of DpcQueue, add the DPC to KeGetCurrentPrcb()->DpcListHead
// Signal the Dpc handling code there's work to do
if (!IsDpcActive()) {
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
}
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
// OpenXbox has this instead:
// if (!pKPRCB->DpcRoutineActive && !pKPRCB->DpcInterruptRequested) {
// pKPRCB->DpcInterruptRequested = TRUE;
}
else {
LeaveCriticalSection(&(g_DpcData.Lock));
}
// Thread-safety is no longer required anymore
LeaveCriticalSection(&(g_DpcData.Lock));
// TODO : Instead, enable interrupts - use KeLowerIrql(OldIrql) ?
RETURN(NeedsInsertion);
@ -1309,12 +1289,7 @@ XBSYSAPI EXPORTNUM(121) xbox::boolean_xt NTAPI xbox::KeIsExecutingDpc
{
LOG_FUNC();
#if 0
// This is the correct implementation, but it doesn't work because our Prcb is per-thread instead of being per-processor
BOOLEAN ret = (BOOLEAN)KeGetCurrentPrcb()->DpcRoutineActive;
#else
BOOLEAN ret = (BOOLEAN)IsDpcActive();
#endif
RETURN(ret);
}
@ -1371,14 +1346,13 @@ XBSYSAPI EXPORTNUM(123) xbox::long_xt NTAPI xbox::KePulseEvent
}
LONG OldState = Event->Header.SignalState;
KiWaitListLock();
if ((OldState == 0) && (IsListEmpty(&Event->Header.WaitListHead) == FALSE)) {
Event->Header.SignalState = 1;
KiWaitTest(Event, Increment);
std::this_thread::yield();
}
else {
KiWaitListUnlock();
// TODO: KiWaitTest(Event, Increment);
// For now, we just sleep to give other threads time to wake
// KiWaitTest and related functions require correct thread scheduling to implement first
// This will have to wait until CPU emulation at v1.0
Sleep(1);
}
Event->Header.SignalState = 0;
@ -1404,7 +1378,9 @@ XBSYSAPI EXPORTNUM(124) xbox::long_xt NTAPI xbox::KeQueryBasePriorityThread
KIRQL OldIrql;
KiLockDispatcherDatabase(&OldIrql);
long_xt ret = Thread->Priority;
// It cannot fail because all thread handles are created by ob
const auto& nativeHandle = GetNativeHandle<true>(PspGetCurrentThread()->UniqueThread);
long_xt ret = GetThreadPriority(*nativeHandle);
KiUnlockDispatcherDatabase(OldIrql);
@ -1557,14 +1533,12 @@ XBSYSAPI EXPORTNUM(132) xbox::long_xt NTAPI xbox::KeReleaseSemaphore
}
Semaphore->Header.SignalState = adjusted_signalstate;
KiWaitListLock();
//TODO: Implement KiWaitTest
#if 0
if ((initial_state == 0) && (IsListEmpty(&Semaphore->Header.WaitListHead) == FALSE)) {
KiWaitTest(&Semaphore->Header, Increment);
std::this_thread::yield();
}
else {
KiWaitListUnlock();
}
#endif
if (Wait) {
PKTHREAD current_thread = KeGetCurrentThread();
@ -1778,29 +1752,11 @@ XBSYSAPI EXPORTNUM(140) xbox::ulong_xt NTAPI xbox::KeResumeThread
{
LOG_FUNC_ONE_ARG(Thread);
KIRQL OldIrql;
KiLockDispatcherDatabase(&OldIrql);
NTSTATUS ret = X_STATUS_SUCCESS;
char_xt OldCount = Thread->SuspendCount;
if (OldCount != 0) {
--Thread->SuspendCount;
if (Thread->SuspendCount == 0) {
#if 0
++Thread->SuspendSemaphore.Header.SignalState;
KiWaitListLock();
KiWaitTest(&Thread->SuspendSemaphore, 0);
std::this_thread::yield();
#else
if (const auto &nativeHandle = GetNativeHandle<true>(reinterpret_cast<PETHREAD>(Thread)->UniqueThread)) {
ResumeThread(*nativeHandle);
}
#endif
}
}
LOG_UNIMPLEMENTED();
KiUnlockDispatcherDatabase(OldIrql);
RETURN(OldCount);
RETURN(ret);
}
XBSYSAPI EXPORTNUM(141) xbox::PLIST_ENTRY NTAPI xbox::KeRundownQueue
@ -1842,15 +1798,25 @@ XBSYSAPI EXPORTNUM(143) xbox::long_xt NTAPI xbox::KeSetBasePriorityThread
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Thread)
LOG_FUNC_ARG(Priority)
LOG_FUNC_ARG_OUT(Thread)
LOG_FUNC_ARG_OUT(Priority)
LOG_FUNC_END;
KIRQL oldIRQL;
KiLockDispatcherDatabase(&oldIRQL);
Thread->Priority = Priority;
long_xt ret = Thread->Priority;
// It cannot fail because all thread handles are created by ob
const auto &nativeHandle = GetNativeHandle<true>(PspGetCurrentThread()->UniqueThread);
LONG ret = GetThreadPriority(*nativeHandle);
// This would work normally, but it will slow down the emulation,
// don't do that if the priority is higher then normal (so our own)!
if (Priority <= THREAD_PRIORITY_NORMAL) {
BOOL result = SetThreadPriority(*nativeHandle, Priority);
if (!result) {
EmuLog(LOG_LEVEL::WARNING, "SetThreadPriority failed: %s", WinError2Str().c_str());
}
}
KiUnlockDispatcherDatabase(oldIRQL);
@ -1871,9 +1837,17 @@ XBSYSAPI EXPORTNUM(144) xbox::boolean_xt NTAPI xbox::KeSetDisableBoostThread
KIRQL oldIRQL;
KiLockDispatcherDatabase(&oldIRQL);
// It cannot fail because all thread handles are created by ob
const auto &nativeHandle = GetNativeHandle<true>(PspGetCurrentThread()->UniqueThread);
boolean_xt prevDisableBoost = Thread->DisableBoost;
Thread->DisableBoost = (CHAR)Disable;
BOOL bRet = SetThreadPriorityBoost(*nativeHandle, Disable);
if (!bRet) {
EmuLog(LOG_LEVEL::WARNING, "SetThreadPriorityBoost failed: %s", WinError2Str().c_str());
}
KiUnlockDispatcherDatabase(oldIRQL);
RETURN(prevDisableBoost);
@ -1908,9 +1882,7 @@ XBSYSAPI EXPORTNUM(145) xbox::long_xt NTAPI xbox::KeSetEvent
}
LONG OldState = Event->Header.SignalState;
KiWaitListLock();
if (IsListEmpty(&Event->Header.WaitListHead) != FALSE) {
KiWaitListUnlock();
Event->Header.SignalState = 1;
} else {
PKWAIT_BLOCK WaitBlock = CONTAINING_RECORD(Event->Header.WaitListHead.Flink, KWAIT_BLOCK, WaitListEntry);
@ -1918,14 +1890,16 @@ XBSYSAPI EXPORTNUM(145) xbox::long_xt NTAPI xbox::KeSetEvent
(WaitBlock->WaitType != WaitAny)) {
if (OldState == 0) {
Event->Header.SignalState = 1;
KiWaitTest(Event, Increment);
}
else {
KiWaitListUnlock();
// TODO: KiWaitTest(Event, Increment);
// For now, we just sleep to give other threads time to wake
// See KePulseEvent
Sleep(1);
}
} else {
KiUnwaitThread(WaitBlock->Thread, (NTSTATUS)WaitBlock->WaitKey, Increment);
KiWaitListUnlock();
// TODO: KiUnwaitThread(WaitBlock->Thread, (NTSTATUS)WaitBlock->WaitKey, Increment);
// For now, we just sleep to give other threads time to wake
// See KePulseEvent
Sleep(1);
}
}
@ -1962,7 +1936,6 @@ XBSYSAPI EXPORTNUM(146) xbox::void_xt NTAPI xbox::KeSetEventBoostPriority
return;
}
KiWaitListLock();
if (IsListEmpty(&Event->Header.WaitListHead) != FALSE) {
Event->Header.SignalState = 1;
} else {
@ -1973,9 +1946,11 @@ XBSYSAPI EXPORTNUM(146) xbox::void_xt NTAPI xbox::KeSetEventBoostPriority
}
WaitThread->Quantum = WaitThread->ApcState.Process->ThreadQuantum;
KiUnwaitThread(WaitThread, X_STATUS_SUCCESS, 1);
// TODO: KiUnwaitThread(WaitThread, X_STATUS_SUCCESS, 1);
// For now, we just sleep to give other threads time to wake
// See KePulseEvent
Sleep(1);
}
KiWaitListUnlock();
KiUnlockDispatcherDatabase(OldIrql);
}
@ -2007,8 +1982,8 @@ XBSYSAPI EXPORTNUM(148) xbox::boolean_xt NTAPI xbox::KeSetPriorityThread
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Thread)
LOG_FUNC_ARG(Priority)
LOG_FUNC_ARG_OUT(Thread)
LOG_FUNC_ARG_OUT(Priority)
LOG_FUNC_END;
LOG_UNIMPLEMENTED();
@ -2121,38 +2096,11 @@ XBSYSAPI EXPORTNUM(152) xbox::ulong_xt NTAPI xbox::KeSuspendThread
{
LOG_FUNC_ONE_ARG(Thread);
KIRQL OldIrql;
KiLockDispatcherDatabase(&OldIrql);
NTSTATUS ret = X_STATUS_SUCCESS;
char_xt OldCount = Thread->SuspendCount;
if (OldCount == X_MAXIMUM_SUSPEND_COUNT) {
KiUnlockDispatcherDatabase(OldIrql);
RETURN(X_STATUS_SUSPEND_COUNT_EXCEEDED);
}
LOG_UNIMPLEMENTED();
if (Thread->ApcState.ApcQueueable == TRUE) {
++Thread->SuspendCount;
if (OldCount == 0) {
#if 0
if (KiInsertQueueApc(&Thread->SuspendApc, 0) == FALSE) {
--Thread->SuspendSemaphore.Header.SignalState;
}
#else
// JSRF creates a thread at 0x0013BC30 and then it attempts to continuously suspend/resume it. Unfortunately, this thread performs a never ending loop (and
// terminates if it ever exit the loop), and never calls any kernel functions in the middle. This means that our suspend APC will never be executed and so
// we cannot suspend such thread. Thus, we will always have to rely on the host to do the suspension, as long as we do direct execution. Note that this is
// a general issue for all kernel APCs too.
if (const auto &nativeHandle = GetNativeHandle<true>(reinterpret_cast<PETHREAD>(Thread)->UniqueThread)) {
SuspendThread(*nativeHandle);
}
#endif
}
}
KiUnlockDispatcherDatabase(OldIrql);
RETURN(OldCount);
RETURN(ret);
}
// ******************************************************************
@ -2248,13 +2196,15 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
// Wait Loop
// This loop ends
PLARGE_INTEGER OriginalTime = Timeout;
PKWAIT_BLOCK WaitBlock;
LARGE_INTEGER DueTime, NewTime;
KWAIT_BLOCK StackWaitBlock;
PKWAIT_BLOCK WaitBlock = &StackWaitBlock;
BOOLEAN WaitSatisfied;
NTSTATUS WaitStatus;
PKMUTANT ObjectMutant;
// Hack variable (remove this when the thread scheduler is here)
bool timeout_set = false;
do {
Thread->WaitBlockList = WaitBlockArray;
// Check if we need to let an APC run. This should immediately trigger APC interrupt via a call to UnlockDispatcherDatabase
if (Thread->ApcState.KernelApcPending && (Thread->WaitIrql < APC_LEVEL)) {
KiUnlockDispatcherDatabase(Thread->WaitIrql);
@ -2313,7 +2263,7 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
// Check if the wait can be satisfied immediately
if ((WaitType == WaitAll) && (WaitSatisfied)) {
WaitBlock->NextWaitBlock = &WaitBlockArray[0];
KiWaitSatisfyAllAndLock(WaitBlock);
KiWaitSatisfyAll(WaitBlock);
WaitStatus = (NTSTATUS)Thread->WaitStatus;
goto NoWait;
}
@ -2328,20 +2278,36 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
goto NoWait;
}
// Setup a timer for the thread
KiTimerLock();
PKTIMER Timer = &Thread->Timer;
PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock;
WaitBlock->NextWaitBlock = WaitTimer;
Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry;
Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry;
WaitTimer->NextWaitBlock = WaitBlock;
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
// Setup a timer for the thread but only once (for now)
if (!timeout_set) {
KiTimerLock();
PKTIMER Timer = &Thread->Timer;
PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock;
WaitBlock->NextWaitBlock = WaitTimer;
Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry;
Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry;
WaitTimer->NextWaitBlock = WaitBlock;
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
KiTimerUnlock();
goto NoWait;
}
// Boring, ensure that we only set the thread timer once. Otherwise, this will cause to insert the same
// thread timer over and over in the timer list, which will prevent KiTimerExpiration from removing these
// duplicated timers and thus it will attempt to endlessly remove the same unremoved timers, causing a deadlock.
// This can be removed once KiSwapThread and the kernel/user APCs are implemented
timeout_set = true;
DueTime.QuadPart = Timer->DueTime.QuadPart;
KiTimerUnlock();
}
// KiTimerExpiration has removed the timer but the objects were not signaled, so we have a timeout
// (remove this when the thread scheduler is here)
if (Thread->Timer.Header.Inserted == FALSE) {
WaitStatus = (NTSTATUS)(STATUS_TIMEOUT);
goto NoWait;
}
KiTimerUnlock();
}
else {
WaitBlock->NextWaitBlock = WaitBlock;
@ -2349,13 +2315,12 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
WaitBlock->NextWaitBlock = &WaitBlockArray[0];
WaitBlock = &WaitBlockArray[0];
KiWaitListLock();
do {
ObjectMutant = (PKMUTANT)WaitBlock->Object;
InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
//InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
WaitBlock = WaitBlock->NextWaitBlock;
} while (WaitBlock != &WaitBlockArray[0]);
KiWaitListUnlock();
/*
TODO: We can't implement this and the return values until we have our own thread scheduler
@ -2363,6 +2328,9 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
This code can all be enabled once we have CPU emulation and our own scheduler in v1.0
*/
// Insert the WaitBlock
//InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
// If the current thread is processing a queue object, wake other treads using the same queue
PRKQUEUE Queue = (PRKQUEUE)Thread->Queue;
if (Queue != NULL) {
@ -2374,7 +2342,7 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
Thread->WaitMode = WaitMode;
Thread->WaitReason = (UCHAR)WaitReason;
Thread->WaitTime = KeTickCount;
Thread->State = Waiting;
//Thread->State = Waiting;
//KiInsertWaitList(WaitMode, Thread);
//WaitStatus = (NTSTATUS)KiSwapThread();
@ -2389,15 +2357,12 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
//}
// TODO: Remove this after we have our own scheduler and the above is implemented
WaitStatus = WaitApc<false>([](PKTHREAD Thread) -> std::optional<ntstatus_xt> {
if (Thread->State == Ready) {
// We have been readied to resume execution, so exit the wait
return std::make_optional<ntstatus_xt>(Thread->WaitStatus);
}
return std::nullopt;
}, Timeout, Alertable, WaitMode, Thread);
Sleep(0);
break;
// Reduce the timout if necessary
if (Timeout != nullptr) {
Timeout = KiComputeWaitInterval(OriginalTime, &DueTime, &NewTime);
}
}
// Raise IRQL to DISPATCH_LEVEL and lock the database (only if it's not already at this level)
@ -2412,14 +2377,10 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
// The waiting thead has been alerted, or an APC needs to be delivered
// So unlock the dispatcher database, lower the IRQ and return the status
Thread->State = Running;
KiUnlockDispatcherDatabase(Thread->WaitIrql);
#if 0
// No need for this at the moment, since WaitApc already executes user APCs
if (WaitStatus == X_STATUS_USER_APC) {
KiExecuteUserApc();
}
#endif
RETURN(WaitStatus);
@ -2428,7 +2389,14 @@ NoWait:
// Unlock the database and return the status
//TODO: KiAdjustQuantumThread(Thread);
Thread->State = Running;
// Don't forget to remove the thread timer if the objects were signaled before the timer expired
// (remove this when the thread scheduler is here)
if (timeout_set && Thread->Timer.Header.Inserted == TRUE) {
KiTimerLock();
KxRemoveTreeTimer(&Thread->Timer);
KiTimerUnlock();
}
KiUnlockDispatcherDatabase(Thread->WaitIrql);
if (WaitStatus == X_STATUS_USER_APC) {
@ -2470,9 +2438,12 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
// Wait Loop
// This loop ends
PLARGE_INTEGER OriginalTime = Timeout;
LARGE_INTEGER DueTime, NewTime;
KWAIT_BLOCK StackWaitBlock;
PKWAIT_BLOCK WaitBlock = &StackWaitBlock;
ntstatus_xt WaitStatus;
NTSTATUS WaitStatus;
// Hack variable (remove this when the thread scheduler is here)
bool timeout_set = false;
do {
// Check if we need to let an APC run. This should immediately trigger APC interrupt via a call to UnlockDispatcherDatabase
if (Thread->ApcState.KernelApcPending && (Thread->WaitIrql < APC_LEVEL)) {
@ -2520,20 +2491,36 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
goto NoWait;
}
// Setup a timer for the thread
KiTimerLock();
PKTIMER Timer = &Thread->Timer;
PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock;
WaitBlock->NextWaitBlock = WaitTimer;
Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry;
Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry;
WaitTimer->NextWaitBlock = WaitBlock;
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
// Setup a timer for the thread but only once (for now)
if (!timeout_set) {
KiTimerLock();
PKTIMER Timer = &Thread->Timer;
PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock;
WaitBlock->NextWaitBlock = WaitTimer;
Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry;
Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry;
WaitTimer->NextWaitBlock = WaitBlock;
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
KiTimerUnlock();
goto NoWait;
}
// Boring, ensure that we only set the thread timer once. Otherwise, this will cause to insert the same
// thread timer over and over in the timer list, which will prevent KiTimerExpiration from removing these
// duplicated timers and thus it will attempt to endlessly remove the same unremoved timers, causing a deadlock.
// This can be removed once KiSwapThread and the kernel/user APCs are implemented
timeout_set = true;
DueTime.QuadPart = Timer->DueTime.QuadPart;
KiTimerUnlock();
}
// KiTimerExpiration has removed the timer but the object was not signaled, so we have a timeout
// (remove this when the thread scheduler is here)
if (Thread->Timer.Header.Inserted == FALSE) {
WaitStatus = (NTSTATUS)(STATUS_TIMEOUT);
goto NoWait;
}
KiTimerUnlock();
}
else {
WaitBlock->NextWaitBlock = WaitBlock;
@ -2545,6 +2532,9 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
This code can all be enabled once we have CPU emulation and our own scheduler in v1.0
*/
// Insert the WaitBlock
//InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
// If the current thread is processing a queue object, wake other treads using the same queue
PRKQUEUE Queue = (PRKQUEUE)Thread->Queue;
if (Queue != NULL) {
@ -2556,7 +2546,7 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
Thread->WaitMode = WaitMode;
Thread->WaitReason = (UCHAR)WaitReason;
Thread->WaitTime = KeTickCount;
Thread->State = Waiting;
// TODO: Thread->State = Waiting;
//KiInsertWaitList(WaitMode, Thread);
/*
@ -2571,21 +2561,13 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
return WaitStatus;
} */
// Insert the WaitBlock
KiWaitListLock();
InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
KiWaitListUnlock();
// TODO: Remove this after we have our own scheduler and the above is implemented
WaitStatus = WaitApc<false>([](PKTHREAD Thread) -> std::optional<ntstatus_xt> {
if (Thread->State == Ready) {
// We have been readied to resume execution, so exit the wait
return std::make_optional<ntstatus_xt>(Thread->WaitStatus);
}
return std::nullopt;
}, Timeout, Alertable, WaitMode, Thread);
Sleep(0);
break;
// Reduce the timout if necessary
if (Timeout != nullptr) {
Timeout = KiComputeWaitInterval(OriginalTime, &DueTime, &NewTime);
}
}
// Raise IRQL to DISPATCH_LEVEL and lock the database
@ -2600,14 +2582,10 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
// The waiting thead has been alerted, or an APC needs to be delivered
// So unlock the dispatcher database, lower the IRQ and return the status
Thread->State = Running;
KiUnlockDispatcherDatabase(Thread->WaitIrql);
#if 0
// No need for this at the moment, since WaitApc already executes user APCs
if (WaitStatus == X_STATUS_USER_APC) {
KiExecuteUserApc();
}
#endif
RETURN(WaitStatus);
@ -2616,7 +2594,14 @@ NoWait:
// Unlock the database and return the status
//TODO: KiAdjustQuantumThread(Thread);
Thread->State = Running;
// Don't forget to remove the thread timer if the object was signaled before the timer expired
// (remove this when the thread scheduler is here)
if (timeout_set && Thread->Timer.Header.Inserted == TRUE) {
KiTimerLock();
KxRemoveTreeTimer(&Thread->Timer);
KiTimerUnlock();
}
KiUnlockDispatcherDatabase(Thread->WaitIrql);
if (WaitStatus == X_STATUS_USER_APC) {

View file

@ -27,8 +27,6 @@
namespace xbox
{
extern std::atomic_flag KeSystemTimeChanged;
void_xt NTAPI KeSetSystemTime
(
IN PLARGE_INTEGER NewTime,
@ -52,16 +50,5 @@ namespace xbox
IN PKPROCESS Process
);
xbox::void_xt KeResumeThreadEx
(
IN PKTHREAD Thread
);
xbox::void_xt KeSuspendThreadEx
(
IN PKTHREAD Thread
);
void_xt KeEmptyQueueApc();
void_xt KeWaitForDpc();
}

View file

@ -59,8 +59,6 @@
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
*/
// Also from ReactOS: KiWaitTest, KiWaitSatisfyAll, KiUnwaitThread, KiUnlinkThread
// COPYING file:
/*
GNU GENERAL PUBLIC LICENSE
@ -86,18 +84,15 @@ the said software).
#include "Logging.h" // For LOG_FUNC()
#include "EmuKrnl.h" // for the list support functions
#include "EmuKrnlKi.h"
#include "EmuKrnlKe.h"
#define MAX_TIMER_DPCS 16
#define ASSERT_TIMER_LOCKED assert(KiTimerMtx.Acquired > 0)
#define ASSERT_WAIT_LIST_LOCKED assert(KiWaitListMtx.Acquired > 0)
xbox::KPROCESS KiUniqueProcess;
const xbox::ulong_xt CLOCK_TIME_INCREMENT = 0x2710;
xbox::KDPC KiTimerExpireDpc;
xbox::KI_TIMER_LOCK KiTimerMtx;
xbox::KI_WAIT_LIST_LOCK KiWaitListMtx;
xbox::KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE];
xbox::LIST_ENTRY KiWaitInListHead;
std::mutex xbox::KiApcListMtx;
@ -134,81 +129,55 @@ xbox::void_xt xbox::KiTimerUnlock()
KiTimerMtx.Mtx.unlock();
}
xbox::void_xt xbox::KiWaitListLock()
xbox::void_xt xbox::KiClockIsr
(
unsigned int ScalingFactor
)
{
KiWaitListMtx.Mtx.lock();
KiWaitListMtx.Acquired++;
}
xbox::void_xt xbox::KiWaitListUnlock()
{
KiWaitListMtx.Acquired--;
KiWaitListMtx.Mtx.unlock();
}
xbox::void_xt xbox::KiClockIsr(ulonglong_xt TotalUs)
{
LARGE_INTEGER InterruptTime, SystemTime;
KIRQL OldIrql;
LARGE_INTEGER InterruptTime;
LARGE_INTEGER HostSystemTime;
ULONG Hand;
DWORD OldKeTickCount;
static uint64_t LostUs;
uint64_t TotalMs = TotalUs / 1000;
LostUs += (TotalUs - TotalMs * 1000);
uint64_t RecoveredMs = LostUs / 1000;
TotalMs += RecoveredMs;
LostUs -= (RecoveredMs * 1000);
OldIrql = KfRaiseIrql(CLOCK_LEVEL);
// Update the interrupt time
InterruptTime.u.LowPart = KeInterruptTime.LowPart;
InterruptTime.u.HighPart = KeInterruptTime.High1Time;
InterruptTime.QuadPart += (CLOCK_TIME_INCREMENT * TotalMs);
InterruptTime.QuadPart += (CLOCK_TIME_INCREMENT * ScalingFactor);
KeInterruptTime.High2Time = InterruptTime.u.HighPart;
KeInterruptTime.LowPart = InterruptTime.u.LowPart;
KeInterruptTime.High1Time = InterruptTime.u.HighPart;
// Update the system time
if (KeSystemTimeChanged.test()) [[unlikely]] {
KeSystemTimeChanged.clear();
LARGE_INTEGER HostSystemTime, OldSystemTime;
GetSystemTimeAsFileTime((LPFILETIME)&HostSystemTime);
xbox::KeSystemTime.High2Time = HostSystemTime.u.HighPart;
xbox::KeSystemTime.LowPart = HostSystemTime.u.LowPart;
xbox::KeSystemTime.High1Time = HostSystemTime.u.HighPart;
KeSetSystemTime(&HostSystemTime, &OldSystemTime);
}
else {
SystemTime.u.LowPart = KeSystemTime.LowPart;
SystemTime.u.HighPart = KeSystemTime.High1Time;
SystemTime.QuadPart += (CLOCK_TIME_INCREMENT * TotalMs);
KeSystemTime.High2Time = SystemTime.u.HighPart;
KeSystemTime.LowPart = SystemTime.u.LowPart;
KeSystemTime.High1Time = SystemTime.u.HighPart;
}
// NOTE: I'm not sure if we should round down the host system time to the nearest multiple
// of the Xbox clock increment...
GetSystemTimeAsFileTime((LPFILETIME)&HostSystemTime);
HostSystemTime.QuadPart += HostSystemTimeDelta.load();
KeSystemTime.High2Time = HostSystemTime.u.HighPart;
KeSystemTime.LowPart = HostSystemTime.u.LowPart;
KeSystemTime.High1Time = HostSystemTime.u.HighPart;
// Update the tick counter
OldKeTickCount = KeTickCount;
KeTickCount += static_cast<dword_xt>(TotalMs);
KeTickCount += ScalingFactor;
// Because this function must be fast to continuously update the kernel clocks, if somebody else is currently
// holding the lock, we won't wait and instead skip the check of the timers for this cycle
if (KiTimerMtx.Mtx.try_lock()) {
KiTimerMtx.Acquired++;
// Check if a timer has expired
// On real hw, this is called every ms, so it only needs to check a single timer index. However, testing on the emulator shows that this can have a delay
// larger than a ms. If we only check the index corresponding to OldKeTickCount, then we will miss timers that might have expired already, causing an unpredictable
// delay on threads that are waiting with those timeouts
dword_xt EndKeTickCount = (KeTickCount - OldKeTickCount) >= TIMER_TABLE_SIZE ? OldKeTickCount + TIMER_TABLE_SIZE : KeTickCount;
for (dword_xt i = OldKeTickCount; i < EndKeTickCount; ++i) {
Hand = i & (TIMER_TABLE_SIZE - 1);
if (KiTimerTableListHead[Hand].Entry.Flink != &KiTimerTableListHead[Hand].Entry &&
(ULONGLONG)InterruptTime.QuadPart >= KiTimerTableListHead[Hand].Time.QuadPart) {
KeInsertQueueDpc(&KiTimerExpireDpc, (PVOID)OldKeTickCount, (PVOID)EndKeTickCount);
break;
}
Hand = OldKeTickCount & (TIMER_TABLE_SIZE - 1);
if (KiTimerTableListHead[Hand].Entry.Flink != &KiTimerTableListHead[Hand].Entry &&
(ULONGLONG)InterruptTime.QuadPart >= KiTimerTableListHead[Hand].Time.QuadPart) {
KeInsertQueueDpc(&KiTimerExpireDpc, (PVOID)Hand, 0);
}
KiTimerMtx.Acquired--;
KiTimerMtx.Mtx.unlock();
}
KfLowerIrql(OldIrql);
}
xbox::void_xt NTAPI xbox::KiCheckTimerTable
@ -359,8 +328,8 @@ xbox::boolean_xt FASTCALL xbox::KiInsertTimerTable
IN xbox::ulong_xt Hand
)
{
ULARGE_INTEGER InterruptTime;
ULONGLONG DueTime = Timer->DueTime.QuadPart;
LARGE_INTEGER InterruptTime;
LONGLONG DueTime = Timer->DueTime.QuadPart;
BOOLEAN Expired = FALSE;
PLIST_ENTRY ListHead, NextEntry;
PKTIMER CurrentTimer;
@ -383,7 +352,7 @@ xbox::boolean_xt FASTCALL xbox::KiInsertTimerTable
CurrentTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
/* Now check if we can fit it before */
if (DueTime >= CurrentTimer->DueTime.QuadPart) break;
if ((ULONGLONG)DueTime >= CurrentTimer->DueTime.QuadPart) break;
/* Keep looping */
NextEntry = NextEntry->Blink;
@ -399,10 +368,6 @@ xbox::boolean_xt FASTCALL xbox::KiInsertTimerTable
KiTimerTableListHead[Hand].Time.QuadPart = DueTime;
/* Make sure it hasn't expired already */
// NOTE: DueTime must be unsigned so that we can perform un unsigned comparison with the interrupt time. Otherwise, if DueTime is very large, it will be
// interpreted as a very small negative number, which will cause the function to think the timer has already expired, when it didn't. Test case: Metal Slug 3.
// It uses KeDelayExecutionThread with a relative timeout of 0x8000000000000000, which is then interpreted here as a negative number that immediately satisfies
// the wait. The title crashes shortly after, since the wait was supposed to end with a user APC queued by NtQueueApcThread instead
InterruptTime.QuadPart = KeQueryInterruptTime();
if (DueTime <= InterruptTime.QuadPart) {
EmuLog(LOG_LEVEL::DEBUG, "Timer %p already expired", Timer);
@ -519,20 +484,26 @@ xbox::boolean_xt FASTCALL xbox::KiSignalTimer
Timer->Header.SignalState = TRUE;
/* Check if the timer has waiters */
KiWaitListLock();
if (!IsListEmpty(&Timer->Header.WaitListHead))
{
KiWaitTest(Timer, 0);
}
else {
KiWaitListUnlock();
/* Check the type of event */
if (Timer->Header.Type == TimerNotificationObject)
{
/* Unwait the thread */
// KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT);
}
else
{
/* Otherwise unwait the thread and signal the timer */
// KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT);
}
}
/* Check if we have a period */
if (Period)
{
/* Calculate the interval and insert the timer */
Interval.QuadPart = Period * -10000LL;
Interval.QuadPart = Int32x32To64(Period, -10000);
while (!KiInsertTreeTimer(Timer, Interval));
}
@ -561,7 +532,7 @@ xbox::void_xt NTAPI xbox::KiTimerExpiration
{
ULARGE_INTEGER SystemTime, InterruptTime;
LARGE_INTEGER Interval;
LONG i;
LONG Limit, Index, i;
ULONG Timers, ActiveTimers, DpcCalls;
PLIST_ENTRY ListHead, NextEntry;
KIRQL OldIrql;
@ -573,25 +544,34 @@ xbox::void_xt NTAPI xbox::KiTimerExpiration
/* Query system and interrupt time */
KeQuerySystemTime((PLARGE_INTEGER)&SystemTime);
InterruptTime.QuadPart = KeQueryInterruptTime();
Limit = KeTickCount;
/* Get the index of the timer and normalize it */
dword_xt OldKeTickCount = PtrToLong(SystemArgument1);
dword_xt EndKeTickCount = PtrToLong(SystemArgument2);
Index = PtrToLong(SystemArgument1);
if ((Limit - Index) >= TIMER_TABLE_SIZE)
{
/* Normalize it */
Limit = Index + TIMER_TABLE_SIZE - 1;
}
/* Setup index and actual limit */
Index--;
Limit &= (TIMER_TABLE_SIZE - 1);
/* Setup accounting data */
DpcCalls = 0;
Timers = 24;
ActiveTimers = 4;
/* Lock the Database */
/* Lock the Database and Raise IRQL */
KiTimerLock();
KiLockDispatcherDatabase(&OldIrql);
/* Start expiration loop */
for (dword_xt i = OldKeTickCount; i < EndKeTickCount; ++i)
do
{
/* Get the current index */
dword_xt Index = i & (TIMER_TABLE_SIZE - 1);
Index = (Index + 1) & (TIMER_TABLE_SIZE - 1);
/* Get list pointers and loop the list */
ListHead = &KiTimerTableListHead[Index].Entry;
@ -619,20 +599,26 @@ xbox::void_xt NTAPI xbox::KiTimerExpiration
Period = Timer->Period;
/* Check if there are any waiters */
KiWaitListLock();
if (!IsListEmpty(&Timer->Header.WaitListHead))
{
KiWaitTest(Timer, 0);
}
else {
KiWaitListUnlock();
/* Check the type of event */
if (Timer->Header.Type == TimerNotificationObject)
{
/* Unwait the thread */
// KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT);
}
else
{
/* Otherwise unwait the thread and signal the timer */
// KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT);
}
}
/* Check if we have a period */
if (Period)
{
/* Calculate the interval and insert the timer */
Interval.QuadPart = Period * -10000LL;
Interval.QuadPart = Int32x32To64(Period, -10000);
while (!KiInsertTreeTimer(Timer, Interval));
}
@ -723,7 +709,7 @@ xbox::void_xt NTAPI xbox::KiTimerExpiration
break;
}
}
}
} while (Index != Limit);
/* Verify the timer table, on a debug kernel only */
if (g_bIsDebugKernel) {
@ -751,17 +737,17 @@ xbox::void_xt NTAPI xbox::KiTimerExpiration
);
}
KiTimerUnlock();
/* Lower IRQL if we need to */
if (OldIrql != DISPATCH_LEVEL) {
KfLowerIrql(OldIrql);
}
KiTimerUnlock();
}
else
{
/* Unlock the dispatcher */
KiTimerUnlock();
KiUnlockDispatcherDatabase(OldIrql);
KiTimerUnlock();
}
}
@ -805,20 +791,26 @@ xbox::void_xt FASTCALL xbox::KiTimerListExpire
Period = Timer->Period;
/* Check if there's any waiters */
KiWaitListLock();
if (!IsListEmpty(&Timer->Header.WaitListHead))
{
KiWaitTest(Timer, 0);
}
else {
KiWaitListUnlock();
/* Check the type of event */
if (Timer->Header.Type == TimerNotificationObject)
{
/* Unwait the thread */
// KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT);
}
else
{
/* Otherwise unwait the thread and signal the timer */
// KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT);
}
}
/* Check if we have a period */
if (Period)
{
/* Calculate the interval and insert the timer */
Interval.QuadPart = Period * -10000LL;
Interval.QuadPart = Int32x32To64(Period, -10000);
while (!KiInsertTreeTimer(Timer, Interval));
}
@ -834,8 +826,6 @@ xbox::void_xt FASTCALL xbox::KiTimerListExpire
}
}
KiTimerUnlock();
/* Check if we still have DPC entries */
if (DpcCalls)
{
@ -859,14 +849,39 @@ xbox::void_xt FASTCALL xbox::KiTimerListExpire
/* Lower IRQL */
KfLowerIrql(OldIrql);
KiTimerUnlock();
}
else
{
/* Unlock the dispatcher */
KiUnlockDispatcherDatabase(OldIrql);
KiTimerUnlock();
}
}
xbox::void_xt FASTCALL xbox::KiWaitSatisfyAll
(
IN xbox::PKWAIT_BLOCK WaitBlock
)
{
PKMUTANT Object;
PRKTHREAD Thread;
PKWAIT_BLOCK WaitBlock1;
WaitBlock1 = WaitBlock;
Thread = WaitBlock1->Thread;
do {
if (WaitBlock1->WaitKey != (cshort_xt)STATUS_TIMEOUT) {
Object = (PKMUTANT)WaitBlock1->Object;
KiWaitSatisfyAny(Object, Thread);
}
WaitBlock1 = WaitBlock1->NextWaitBlock;
} while (WaitBlock1 != WaitBlock);
return;
}
template<xbox::MODE ApcMode>
static xbox::void_xt KiExecuteApc()
{
@ -892,13 +907,12 @@ static xbox::void_xt KiExecuteApc()
Apc->Inserted = FALSE;
xbox::KiApcListMtx.unlock();
// This is either KiFreeUserApc, which frees the memory of the apc, or KiSuspendNop, which does nothing
(Apc->KernelRoutine)(Apc, &Apc->NormalRoutine, &Apc->NormalContext, &Apc->SystemArgument1, &Apc->SystemArgument2);
// NOTE: we never use KernelRoutine because that is only used for kernel APCs, which we currently don't use
if (Apc->NormalRoutine != xbox::zeroptr) {
(Apc->NormalRoutine)(Apc->NormalContext, Apc->SystemArgument1, Apc->SystemArgument2);
}
xbox::ExFreePool(Apc);
xbox::KiApcListMtx.lock();
}
@ -952,8 +966,7 @@ xbox::PLARGE_INTEGER FASTCALL xbox::KiComputeWaitInterval
}
// Source: ReactOS
xbox::void_xt NTAPI xbox::KiSuspendNop
(
xbox::void_xt NTAPI xbox::KiSuspendNop(
IN PKAPC Apc,
IN PKNORMAL_ROUTINE* NormalRoutine,
IN PVOID* NormalContext,
@ -961,7 +974,7 @@ xbox::void_xt NTAPI xbox::KiSuspendNop
IN PVOID* SystemArgument2
)
{
/* Does nothing because the memory of the suspend apc is part of kthread */
/* Does nothing */
UNREFERENCED_PARAMETER(Apc);
UNREFERENCED_PARAMETER(NormalRoutine);
UNREFERENCED_PARAMETER(NormalContext);
@ -969,21 +982,8 @@ xbox::void_xt NTAPI xbox::KiSuspendNop
UNREFERENCED_PARAMETER(SystemArgument2);
}
xbox::void_xt NTAPI xbox::KiFreeUserApc
(
IN PKAPC Apc,
IN PKNORMAL_ROUTINE *NormalRoutine,
IN PVOID *NormalContext,
IN PVOID *SystemArgument1,
IN PVOID *SystemArgument2
)
{
ExFreePool(Apc);
}
// Source: ReactOS
xbox::void_xt NTAPI xbox::KiSuspendThread
(
xbox::void_xt NTAPI xbox::KiSuspendThread(
IN PVOID NormalContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
@ -1076,210 +1076,3 @@ xbox::void_xt xbox::KiInitializeContextThread(
/* Save back the new value of the kernel stack. */
Thread->KernelStack = reinterpret_cast<PVOID>(CtxSwitchFrame);
}
xbox::boolean_xt xbox::KiInsertQueueApc
(
IN PRKAPC Apc,
IN KPRIORITY Increment
)
{
PKTHREAD kThread = Apc->Thread;
KiApcListMtx.lock();
if (Apc->Inserted) {
KiApcListMtx.unlock();
return FALSE;
}
InsertTailList(&kThread->ApcState.ApcListHead[Apc->ApcMode], &Apc->ApcListEntry);
Apc->Inserted = TRUE;
KiApcListMtx.unlock();
// We can only attempt to execute the queued apc right away if it is been inserted in the current thread, because otherwise the KTHREAD
// in the fs selector will not be correct
if (Apc->ApcMode == KernelMode) { // kernel apc
kThread->ApcState.KernelApcPending = TRUE;
// NOTE: this is wrong, we should check the thread state instead of just signaling the kernel apc, but we currently
// don't set the appropriate state in kthread
if (kThread == KeGetCurrentThread()) {
KiExecuteKernelApc();
}
}
else if ((kThread->WaitMode == UserMode) && (kThread->Alertable)) { // user apc
kThread->ApcState.UserApcPending = TRUE;
// NOTE: this should also check the thread state
if (kThread == KeGetCurrentThread()) {
KiExecuteUserApc();
}
}
return TRUE;
}
xbox::void_xt xbox::KiWaitTest
(
IN PVOID Object,
IN KPRIORITY Increment
)
{
PLIST_ENTRY WaitEntry, WaitList;
PKWAIT_BLOCK WaitBlock, NextBlock;
PKTHREAD WaitThread;
PKMUTANT FirstObject = (PKMUTANT)Object;
ASSERT_WAIT_LIST_LOCKED;
/* Loop the Wait Entries */
WaitList = &FirstObject->Header.WaitListHead;
WaitEntry = WaitList->Flink;
while ((FirstObject->Header.SignalState > 0) && (WaitEntry != WaitList)) {
/* Get the current wait block */
WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry);
WaitThread = WaitBlock->Thread;
/* Check the current Wait Mode */
if (WaitBlock->WaitType == WaitAny) {
/* Easy case, satisfy only this wait */
KiWaitSatisfyAny(FirstObject, WaitThread);
}
else {
/* WaitAll, check that all the objects are signalled */
NextBlock = WaitBlock->NextWaitBlock;
while (NextBlock != WaitBlock) {
if (NextBlock->WaitKey != X_STATUS_TIMEOUT) {
PKMUTANT Mutant = (PKMUTANT)NextBlock->Object;
// NOTE: we ignore mutants because we forward them to ntdll
if (Mutant->Header.SignalState <= 0) {
// We found at least one object not in the signalled state, so we cannot satisfy the wait
goto NextWaitEntry;
}
}
NextBlock = NextBlock->NextWaitBlock;
}
KiWaitSatisfyAll(WaitBlock);
}
/* Now do the rest of the unwait */
KiUnwaitThread(WaitThread, WaitBlock->WaitKey, Increment);
NextWaitEntry:
WaitEntry = WaitEntry->Flink;
}
KiWaitListUnlock();
}
xbox::void_xt xbox::KiWaitSatisfyAll
(
IN PKWAIT_BLOCK FirstBlock
)
{
PKWAIT_BLOCK WaitBlock = FirstBlock;
PKTHREAD WaitThread = WaitBlock->Thread;
ASSERT_WAIT_LIST_LOCKED;
/* Loop through all the Wait Blocks, and wake each Object */
do {
/* Make sure it hasn't timed out */
if (WaitBlock->WaitKey != X_STATUS_TIMEOUT) {
/* Wake the Object */
KiWaitSatisfyAny((PKMUTANT)WaitBlock->Object, WaitThread);
}
/* Move to the next block */
WaitBlock = WaitBlock->NextWaitBlock;
} while (WaitBlock != FirstBlock);
}
xbox::void_xt xbox::KiWaitSatisfyAllAndLock
(
IN PKWAIT_BLOCK FirstBlock
)
{
KiWaitListLock();
KiWaitSatisfyAll(FirstBlock);
KiWaitListUnlock();
}
xbox::void_xt xbox::KiUnwaitThread
(
IN PKTHREAD Thread,
IN long_ptr_xt WaitStatus,
IN KPRIORITY Increment
)
{
ASSERT_WAIT_LIST_LOCKED;
if (Thread->State != Waiting) {
// Don't do anything if it was already unwaited
return;
}
/* Unlink the thread */
KiUnlinkThread(Thread, WaitStatus);
// We cannot schedule the thread, so we'll just set its state to Ready
Thread->State = Ready;
}
xbox::void_xt xbox::KiUnwaitThreadAndLock
(
IN PKTHREAD Thread,
IN long_ptr_xt WaitStatus,
IN KPRIORITY Increment
)
{
KiWaitListLock();
KiUnwaitThread(Thread, WaitStatus, Increment);
KiWaitListUnlock();
}
xbox::void_xt xbox::KiUnlinkThread
(
IN PKTHREAD Thread,
IN long_ptr_xt WaitStatus
)
{
PKWAIT_BLOCK WaitBlock;
PKTIMER Timer;
ASSERT_WAIT_LIST_LOCKED;
/* Update wait status */
Thread->WaitStatus |= WaitStatus;
/* Remove the Wait Blocks from the list */
WaitBlock = Thread->WaitBlockList;
do {
/* Remove it */
RemoveEntryList(&WaitBlock->WaitListEntry);
/* Go to the next one */
WaitBlock = WaitBlock->NextWaitBlock;
} while (WaitBlock != Thread->WaitBlockList);
#if 0
// Disabled, as we currently don't put threads in the ready list
/* Remove the thread from the wait list! */
if (Thread->WaitListEntry.Flink) {
RemoveEntryList(&Thread->WaitListEntry);
}
#endif
/* Check if there's a Thread Timer */
Timer = &Thread->Timer;
if (Timer->Header.Inserted) {
KiTimerLock();
KxRemoveTreeTimer(Timer);
KiTimerUnlock();
}
#if 0
// Disabled, because we don't support queues
/* Increment the Queue's active threads */
if (Thread->Queue) Thread->Queue->CurrentCount++;
#endif
// Sanity check: set WaitBlockList to nullptr so that we can catch the case where a waiter starts a new wait but forgets to setup a new wait block. This
// way, we will crash instead of silently using the pointer to the old block
Thread->WaitBlockList = zeroptr;
}

View file

@ -47,12 +47,6 @@ namespace xbox
int Acquired;
} KI_TIMER_LOCK;
typedef struct _KI_WAIT_LIST_LOCK
{
std::recursive_mutex Mtx;
int Acquired;
} KI_WAIT_LIST_LOCK;
// NOTE: since the apc list is per-thread, we could also create a different mutex for each kthread
extern std::mutex KiApcListMtx;
@ -62,11 +56,10 @@ namespace xbox
void_xt KiTimerUnlock();
void_xt KiWaitListLock();
void_xt KiWaitListUnlock();
void_xt KiClockIsr(ulonglong_xt TotalUs);
void_xt KiClockIsr
(
IN unsigned int ScalingFactor
);
xbox::void_xt NTAPI KiCheckTimerTable
(
@ -139,7 +132,7 @@ namespace xbox
IN KIRQL OldIrql
);
void_xt KiWaitSatisfyAll
void_xt FASTCALL KiWaitSatisfyAll
(
IN PKWAIT_BLOCK WaitBlock
);
@ -163,8 +156,7 @@ namespace xbox
);
// Source: ReactOS
void_xt NTAPI KiSuspendNop
(
void_xt NTAPI KiSuspendNop(
IN PKAPC Apc,
IN PKNORMAL_ROUTINE* NormalRoutine,
IN PVOID* NormalContext,
@ -172,15 +164,6 @@ namespace xbox
IN PVOID* SystemArgument2
);
void_xt NTAPI KiFreeUserApc
(
IN PKAPC Apc,
IN PKNORMAL_ROUTINE *NormalRoutine,
IN PVOID *NormalContext,
IN PVOID *SystemArgument1,
IN PVOID *SystemArgument2
);
// Source: ReactOS
void_xt NTAPI KiSuspendThread(
IN PVOID NormalContext,
@ -197,48 +180,6 @@ namespace xbox
IN PKSTART_ROUTINE StartRoutine,
IN PVOID StartContext
);
boolean_xt KiInsertQueueApc
(
IN PRKAPC Apc,
IN KPRIORITY Increment
);
void_xt KiWaitTest
(
IN PVOID Object,
IN KPRIORITY Increment
);
void_xt KiWaitSatisfyAll
(
IN PKWAIT_BLOCK FirstBlock
);
void_xt KiWaitSatisfyAllAndLock
(
IN PKWAIT_BLOCK FirstBlock
);
void_xt KiUnwaitThread
(
IN PKTHREAD Thread,
IN long_ptr_xt WaitStatus,
IN KPRIORITY Increment
);
void_xt KiUnwaitThreadAndLock
(
IN PKTHREAD Thread,
IN long_ptr_xt WaitStatus,
IN KPRIORITY Increment
);
void_xt KiUnlinkThread
(
IN PKTHREAD Thread,
IN long_ptr_xt WaitStatus
);
};
extern xbox::KPROCESS KiUniqueProcess;

View file

@ -47,7 +47,6 @@ namespace NtDll
#include "core\kernel\support\EmuFile.h" // For EmuNtSymbolicLinkObject, NtStatusToString(), etc.
#include "core\kernel\memory-manager\VMManager.h" // For g_VMManager
#include "core\kernel\support\NativeHandle.h"
#include "devices\Xbox.h"
#include "CxbxDebugger.h"
#pragma warning(disable:4005) // Ignore redefined status values
@ -59,7 +58,7 @@ namespace NtDll
#include <mutex>
// Prevent setting the system time from multiple threads at the same time
xbox::RTL_CRITICAL_SECTION xbox::NtSystemTimeCritSec;
std::mutex NtSystemTimeMtx;
// ******************************************************************
// * 0x00B8 - NtAllocateVirtualMemory()
@ -1040,7 +1039,7 @@ XBSYSAPI EXPORTNUM(206) xbox::ntstatus_xt NTAPI xbox::NtQueueApcThread
PKAPC Apc = static_cast<PKAPC>(ExAllocatePoolWithTag(sizeof(KAPC), 'pasP'));
if (Apc != zeroptr) {
KeInitializeApc(Apc, &Thread->Tcb, KiFreeUserApc, zeroptr, reinterpret_cast<PKNORMAL_ROUTINE>(ApcRoutine), UserMode, ApcRoutineContext);
KeInitializeApc(Apc, &Thread->Tcb, zeroptr, zeroptr, reinterpret_cast<PKNORMAL_ROUTINE>(ApcRoutine), UserMode, ApcRoutineContext);
if (!KeInsertQueueApc(Apc, ApcStatusBlock, ApcReserved, 0)) {
ExFreePool(Apc);
result = X_STATUS_UNSUCCESSFUL;
@ -1712,16 +1711,6 @@ XBSYSAPI EXPORTNUM(219) xbox::ntstatus_xt NTAPI xbox::NtReadFile
CxbxDebugger::ReportFileRead(FileHandle, Length, Offset);
}
// If we are emulating the Chihiro, we need to hook mbcom
if (g_bIsChihiro && FileHandle == CHIHIRO_MBCOM_HANDLE) {
g_MediaBoard->ComRead(ByteOffset->QuadPart, Buffer, Length);
// Update the Status Block
IoStatusBlock->Status = STATUS_SUCCESS;
IoStatusBlock->Information = Length;
return STATUS_SUCCESS;
}
if (ApcRoutine != nullptr) {
// Pack the original parameters to a wrapped context for a custom APC routine
CxbxIoDispatcherContext* cxbxContext = new CxbxIoDispatcherContext(IoStatusBlock, ApcRoutine, ApcContext);
@ -1867,20 +1856,15 @@ XBSYSAPI EXPORTNUM(224) xbox::ntstatus_xt NTAPI xbox::NtResumeThread
LOG_FUNC_ARG_OUT(PreviousSuspendCount)
LOG_FUNC_END;
PETHREAD Thread;
ntstatus_xt result = ObReferenceObjectByHandle(ThreadHandle, &PsThreadObjectType, reinterpret_cast<PVOID *>(&Thread));
if (!X_NT_SUCCESS(result)) {
RETURN(result);
if (const auto &nativeHandle = GetNativeHandle(ThreadHandle)) {
// Thread handles are created by ob
RETURN(NtDll::NtResumeThread(*nativeHandle, (::PULONG)PreviousSuspendCount));
}
else {
RETURN(X_STATUS_INVALID_HANDLE);
}
ulong_xt PrevSuspendCount = KeResumeThread(&Thread->Tcb);
ObfDereferenceObject(Thread);
if (PreviousSuspendCount) {
*PreviousSuspendCount = PrevSuspendCount;
}
RETURN(X_STATUS_SUCCESS);
// TODO : Once we do our own thread-switching, implement NtResumeThread using KetResumeThread
}
// ******************************************************************
@ -1987,7 +1971,7 @@ XBSYSAPI EXPORTNUM(228) xbox::ntstatus_xt NTAPI xbox::NtSetSystemTime
ret = STATUS_ACCESS_VIOLATION;
}
else {
RtlEnterCriticalSectionAndRegion(&NtSystemTimeCritSec);
NtSystemTimeMtx.lock();
NewSystemTime = *SystemTime;
if (NewSystemTime.u.HighPart > 0 && NewSystemTime.u.HighPart <= 0x20000000) {
/* Convert the time and set it in HAL */
@ -2007,7 +1991,7 @@ XBSYSAPI EXPORTNUM(228) xbox::ntstatus_xt NTAPI xbox::NtSetSystemTime
else {
ret = STATUS_INVALID_PARAMETER;
}
RtlLeaveCriticalSectionAndRegion(&NtSystemTimeCritSec);
NtSystemTimeMtx.unlock();
}
RETURN(ret);
@ -2095,30 +2079,15 @@ XBSYSAPI EXPORTNUM(231) xbox::ntstatus_xt NTAPI xbox::NtSuspendThread
LOG_FUNC_ARG_OUT(PreviousSuspendCount)
LOG_FUNC_END;
PETHREAD Thread;
ntstatus_xt result = ObReferenceObjectByHandle(ThreadHandle, &PsThreadObjectType, reinterpret_cast<PVOID *>(&Thread));
if (!X_NT_SUCCESS(result)) {
RETURN(result);
if (const auto &nativeHandle = GetNativeHandle(ThreadHandle)) {
// Thread handles are created by ob
RETURN(NtDll::NtSuspendThread(*nativeHandle, (::PULONG)PreviousSuspendCount));
}
else {
RETURN(X_STATUS_INVALID_HANDLE);
}
if (Thread != PspGetCurrentThread()) {
if (Thread->Tcb.HasTerminated) {
ObfDereferenceObject(Thread);
RETURN(X_STATUS_THREAD_IS_TERMINATING);
}
}
ulong_xt PrevSuspendCount = KeSuspendThread(&Thread->Tcb);
ObfDereferenceObject(Thread);
if (PrevSuspendCount == X_STATUS_SUSPEND_COUNT_EXCEEDED) {
RETURN(X_STATUS_SUSPEND_COUNT_EXCEEDED);
}
if (PreviousSuspendCount) {
*PreviousSuspendCount = PrevSuspendCount;
}
RETURN(X_STATUS_SUCCESS);
// TODO : Once we do our own thread-switching, implement NtSuspendThread using KeSuspendThread
}
// ******************************************************************
@ -2232,23 +2201,15 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx
if (const auto &nativeHandle = GetNativeHandle(Handles[i])) {
// This is a ob handle, so replace it with its native counterpart
nativeHandles[i] = *nativeHandle;
EmuLog(LOG_LEVEL::DEBUG, "xbox handle: %p", nativeHandles[i]);
}
else {
nativeHandles[i] = Handles[i];
EmuLog(LOG_LEVEL::DEBUG, "native handle: %p", nativeHandles[i]);
}
}
// Because user APCs from NtQueueApcThread are now handled by the kernel, we need to wait for them ourselves
PKTHREAD kThread = KeGetCurrentThread();
kThread->WaitStatus = X_STATUS_SUCCESS;
if (!AddWaitObject(kThread, Timeout)) {
RETURN(X_STATUS_TIMEOUT);
}
xbox::ntstatus_xt ret = WaitApc<true>([Count, &nativeHandles, WaitType, Alertable](xbox::PKTHREAD kThread) -> std::optional<ntstatus_xt> {
xbox::ntstatus_xt ret = WaitApc([Count, &nativeHandles, WaitType, Alertable]() -> std::optional<ntstatus_xt> {
NtDll::LARGE_INTEGER ExpireTime;
ExpireTime.QuadPart = 0;
NTSTATUS Status = NtDll::NtWaitForMultipleObjects(
@ -2260,11 +2221,8 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx
if (Status == STATUS_TIMEOUT) {
return std::nullopt;
}
// If the wait was satisfied with the host, then also unwait the thread on the guest side, to be sure to remove WaitBlocks that might have been added
// to the thread. Test case: Steel Battalion
xbox::KiUnwaitThreadAndLock(kThread, Status, 0);
return std::make_optional<ntstatus_xt>(kThread->WaitStatus);
}, Timeout, Alertable, WaitMode, kThread);
return std::make_optional<ntstatus_xt>(Status);
}, Timeout, Alertable, WaitMode);
RETURN(ret);
}
@ -2311,16 +2269,6 @@ XBSYSAPI EXPORTNUM(236) xbox::ntstatus_xt NTAPI xbox::NtWriteFile
CxbxDebugger::ReportFileWrite(FileHandle, Length, Offset);
}
// If we are emulating the Chihiro, we need to hook mbcom
if (g_bIsChihiro && FileHandle == CHIHIRO_MBCOM_HANDLE) {
g_MediaBoard->ComWrite(ByteOffset->QuadPart, Buffer, Length);
// Update the Status Block
IoStatusBlock->Status = STATUS_SUCCESS;
IoStatusBlock->Information = Length;
return STATUS_SUCCESS;
}
if (ApcRoutine != nullptr) {
// Pack the original parameters to a wrapped context for a custom APC routine
CxbxIoDispatcherContext* cxbxContext = new CxbxIoDispatcherContext(IoStatusBlock, ApcRoutine, ApcContext);

View file

@ -1023,52 +1023,41 @@ XBSYSAPI EXPORTNUM(246) xbox::ntstatus_xt NTAPI xbox::ObReferenceObjectByHandle
PVOID Object;
POBJECT_HEADER ObjectHeader;
// Check if Handle contain special handle for current thread.
if (Handle == NtCurrentThread()) {
// We only accept either thread or null object type.
if ((ObjectType == &PsThreadObjectType) || (!ObjectType)) {
if ((ObjectType == &PsThreadObjectType) || (ObjectType == NULL)) {
Object = PspGetCurrentThread();
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
InterlockedIncrement((::PLONG)(&ObjectHeader->PointerCount));
*ReturnedObject = Object;
return X_STATUS_SUCCESS;
}
else {
} else {
result = STATUS_OBJECT_TYPE_MISMATCH;
}
} else {
Object = ObpGetObjectHandleReference(Handle);
// Check if object is null pointer
if (!Object) {
DWORD flags = 0;
if (Handle == (xbox::HANDLE)-1) {
// bypass hack below check if special handle is NtCurrentProcess.
if (Object != NULL) {
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
if ((ObjectType == ObjectHeader->Type) || (ObjectType == NULL)) {
*ReturnedObject = Object;
return X_STATUS_SUCCESS;
} else {
ObfDereferenceObject(Object);
result = STATUS_OBJECT_TYPE_MISMATCH;
}
} else {
// HACK: Since we forward to NtDll::NtCreateEvent, this *might* be a Windows handle instead of our own
// In this case, we must return the input handle
// Test Case: Xbox Live Dashboard, Network Test (or any other Xbox Live connection)
else if (GetHandleInformation(Handle, &flags)) {
DWORD flags = 0;
if (GetHandleInformation(Handle, &flags)) {
// This was a Windows Handle, so return it.
*ReturnedObject = Handle;
return X_STATUS_SUCCESS;
}
// TODO: Remove above, inside if statement, to leave only result value set here.
result = STATUS_INVALID_HANDLE;
}
// If object is valid, then return object.
else {
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
// Verify if object type do match with found object or any if null object type.
if ((ObjectType == ObjectHeader->Type) || (!ObjectType)) {
*ReturnedObject = Object;
return X_STATUS_SUCCESS;
}
else {
ObfDereferenceObject(Object);
result = STATUS_OBJECT_TYPE_MISMATCH;
}
result = STATUS_INVALID_HANDLE;
}
}

View file

@ -121,8 +121,6 @@ static unsigned int WINAPI PCSTProxy
params.Ethread,
params.TlsDataSize);
xbox::KiExecuteKernelApc();
auto routine = (xbox::PKSYSTEM_ROUTINE)StartFrame->SystemRoutine;
// Debugging notice : When the below line shows up with an Exception dialog and a
// message like: "Exception thrown at 0x00026190 in cxbx.exe: 0xC0000005: Access
@ -408,24 +406,13 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx
assert(dupHandle);
RegisterXboxHandle(eThread->UniqueThread, dupHandle);
eThread->Tcb.Priority = GetThreadPriority(handle);
g_AffinityPolicy->SetAffinityXbox(handle);
// Wait for the initialization of the remaining thread state
KeSuspendThreadEx(&eThread->Tcb);
ResumeThread(handle);
while (eThread->Tcb.State == Initialized) {
std::this_thread::yield();
// Now that ThreadId is populated and affinity is changed, resume the thread (unless the guest passed CREATE_SUSPENDED)
if (!CreateSuspended) {
ResumeThread(handle);
}
// Now that ThreadId is populated and affinity is changed, resume the thread (unless the guest passed CREATE_SUSPENDED), then wait until the new thread has
// finished initialization
if (CreateSuspended) {
KeSuspendThread(&eThread->Tcb);
}
KeResumeThreadEx(&eThread->Tcb);
// Log ThreadID identical to how GetCurrentThreadID() is rendered :
EmuLog(LOG_LEVEL::DEBUG, "Created Xbox proxy thread. Handle : 0x%X, ThreadId : [0x%.4X], Native Handle : 0x%X, Native ThreadId : [0x%.4X]",
*ThreadHandle, eThread->UniqueThread, handle, ThreadId);
@ -506,13 +493,10 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread
KeQuerySystemTime(&eThread->ExitTime);
eThread->ExitStatus = ExitStatus;
eThread->Tcb.Header.SignalState = 1;
KiWaitListLock();
if (!IsListEmpty(&eThread->Tcb.Header.WaitListHead)) {
KiWaitTest((PVOID)&eThread->Tcb, 0);
std::this_thread::yield();
}
else {
KiWaitListUnlock();
// TODO: Implement KiWaitTest's relative objects usage
//KiWaitTest()
assert(0);
}
if (GetNativeHandle(eThread->UniqueThread)) {

View file

@ -89,11 +89,6 @@ xbox::boolean_xt RtlpCaptureStackLimits(
return TRUE;
}
xbox::void_xt xbox::RtlInitSystem()
{
xbox::RtlInitializeCriticalSection(&NtSystemTimeCritSec);
}
// ******************************************************************
// * 0x0104 - RtlAnsiStringToUnicodeString()
// ******************************************************************
@ -113,28 +108,23 @@ XBSYSAPI EXPORTNUM(260) xbox::ntstatus_xt NTAPI xbox::RtlAnsiStringToUnicodeStri
dword_xt total = RtlAnsiStringToUnicodeSize(SourceString);
if (total > 0xffff) {
RETURN(X_STATUS_INVALID_PARAMETER_2);
return X_STATUS_INVALID_PARAMETER_2;
}
DestinationString->Length = (USHORT)(total - sizeof(WCHAR));
if (AllocateDestinationString) {
DestinationString->MaximumLength = (USHORT)total;
if (!(DestinationString->Buffer = (wchar_xt*)ExAllocatePoolWithTag(total, 'grtS'))) {
RETURN(X_STATUS_NO_MEMORY);
if (!(DestinationString->Buffer = (USHORT*)ExAllocatePoolWithTag(total, 'grtS'))) {
return X_STATUS_NO_MEMORY;
}
}
else {
if (total > DestinationString->MaximumLength) {
RETURN(X_STATUS_BUFFER_OVERFLOW);
return X_STATUS_BUFFER_OVERFLOW;
}
}
RtlMultiByteToUnicodeN((PWSTR)DestinationString->Buffer,
(ULONG)DestinationString->Length,
NULL,
SourceString->Buffer,
SourceString->Length);
RtlMultiByteToUnicodeN((PWSTR)DestinationString->Buffer, (ULONG)DestinationString->Length, NULL, SourceString->Buffer, SourceString->Length);
DestinationString->Buffer[DestinationString->Length / sizeof(WCHAR)] = 0;
RETURN(X_STATUS_SUCCESS);
@ -531,31 +521,23 @@ XBSYSAPI EXPORTNUM(270) xbox::long_xt NTAPI xbox::RtlCompareString
LOG_FUNC_ARG(CaseInSensitive)
LOG_FUNC_END;
const USHORT l1 = String1->Length;
const USHORT l2 = String2->Length;
const USHORT maxLen = (l1 <= l2 ? l1 : l2);
LONG result;
const PCHAR str1 = String1->Buffer;
const PCHAR str2 = String2->Buffer;
USHORT l1 = String1->Length;
USHORT l2 = String2->Length;
USHORT maxLen = l1 <= l2 ? l1 : l2;
CHAR *str1 = String1->Buffer;
CHAR *str2 = String2->Buffer;
if (CaseInSensitive) {
for (unsigned i = 0; i < maxLen; i++) {
UCHAR char1 = RtlLowerChar(str1[i]);
UCHAR char2 = RtlLowerChar(str2[i]);
if (char1 != char2) {
RETURN(char1 - char2);
}
}
result = _strnicmp(str1, str2, maxLen);
}
else {
for (unsigned i = 0; i < maxLen; i++) {
if (str1[i] != str2[i]) {
RETURN(str1[i] - str2[i]);
}
}
result = strncmp(str1, str2, maxLen);
}
RETURN(l1 - l2);
RETURN(result);
}
// ******************************************************************
@ -574,32 +556,23 @@ XBSYSAPI EXPORTNUM(271) xbox::long_xt NTAPI xbox::RtlCompareUnicodeString
LOG_FUNC_ARG(CaseInSensitive)
LOG_FUNC_END;
const USHORT l1 = String1->Length;
const USHORT l2 = String2->Length;
const USHORT maxLen = (l1 <= l2 ? l1 : l2) / sizeof(WCHAR);
LONG result;
const wchar_xt* str1 = String1->Buffer;
const wchar_xt* str2 = String2->Buffer;
USHORT l1 = String1->Length;
USHORT l2 = String2->Length;
USHORT maxLen = l1 <= l2 ? l1 : l2;
WCHAR *str1 = (WCHAR*)(String1->Buffer);
WCHAR *str2 = (WCHAR*)(String2->Buffer);
if (CaseInSensitive) {
for (unsigned i = 0; i < maxLen; i++) {
wchar_xt char1 = towlower(str1[i]);
wchar_xt char2 = towlower(str2[i]);
if (char1 != char2) {
RETURN(char1 - char2);
}
}
result = _wcsnicmp(str1, str2, maxLen);
}
else {
for (unsigned i = 0; i < maxLen; i++) {
if (str1[i] != str2[i]) {
RETURN(str1[i] - str2[i]);
}
}
result = wcsncmp(str1, str2, maxLen);
}
RETURN(l1 - l2);
RETURN(result);
}
// ******************************************************************
@ -679,7 +652,7 @@ XBSYSAPI EXPORTNUM(274) xbox::boolean_xt NTAPI xbox::RtlCreateUnicodeString
BOOLEAN result = TRUE;
ULONG bufferSize = (std::u16string(SourceString).length() + 1) * sizeof(WCHAR);
DestinationString->Buffer = (wchar_xt*)ExAllocatePoolWithTag(bufferSize, 'grtS');
DestinationString->Buffer = (USHORT *)ExAllocatePoolWithTag(bufferSize, 'grtS');
if (!DestinationString->Buffer) {
result = FALSE;
}
@ -727,7 +700,7 @@ XBSYSAPI EXPORTNUM(276) xbox::ntstatus_xt NTAPI xbox::RtlDowncaseUnicodeString
if (AllocateDestinationString) {
DestinationString->MaximumLength = SourceString->Length;
DestinationString->Buffer = (wchar_xt*)ExAllocatePoolWithTag((ULONG)DestinationString->MaximumLength, 'grtS');
DestinationString->Buffer = (USHORT*)ExAllocatePoolWithTag((ULONG)DestinationString->MaximumLength, 'grtS');
if (DestinationString->Buffer == NULL) {
return X_STATUS_NO_MEMORY;
}
@ -1200,8 +1173,9 @@ XBSYSAPI EXPORTNUM(290) xbox::void_xt NTAPI xbox::RtlInitUnicodeString
LOG_FUNC_ARG(SourceString)
LOG_FUNC_END;
DestinationString->Buffer = (wchar_xt*)SourceString;
DestinationString->Buffer = (USHORT*)SourceString;
if (SourceString != NULL) {
DestinationString->Buffer = (USHORT*)SourceString;
DestinationString->Length = (USHORT)std::u16string(SourceString).length() * 2;
DestinationString->MaximumLength = DestinationString->Length + 2;
}
@ -1549,7 +1523,6 @@ no_mapping:
#define TICKSPERSEC 10000000
#define TICKSPERMSEC 10000
#define MSECSPERSEC 1000
#define SECSPERDAY 86400
#define SECSPERHOUR 3600
#define SECSPERMIN 60
@ -1568,11 +1541,6 @@ no_mapping:
#define SECS_1601_TO_1980 ((379 * 365 + 91) * (ULONGLONG)SECSPERDAY)
#define TICKS_1601_TO_1980 (SECS_1601_TO_1980 * TICKSPERSEC)
const xbox::LARGE_INTEGER Magic10000 = { .QuadPart = 0xd1b71758e219652ci64 };
#define SHIFT10000 13
xbox::LARGE_INTEGER Magic86400000 = { .QuadPart = 0xc6d750ebfa67b90ei64 };
#define SHIFT86400000 26
static const int MonthLengths[2][MONSPERYEAR] =
{
@ -1626,31 +1594,26 @@ XBSYSAPI EXPORTNUM(304) xbox::boolean_xt NTAPI xbox::RtlTimeFieldsToTime
OUT PLARGE_INTEGER Time
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(TimeFields)
LOG_FUNC_ARG_OUT(Time)
LOG_FUNC_END;
int month, year, cleaps, day;
/* Verify each TimeFields' variables are within range */
if (TimeFields->Millisecond < 0 || TimeFields->Millisecond > 999) {
/* FIXME: normalize the TIME_FIELDS structure here */
/* No, native just returns 0 (error) if the fields are not */
if (TimeFields->Millisecond < 0 || TimeFields->Millisecond > 999 ||
TimeFields->Second < 0 || TimeFields->Second > 59 ||
TimeFields->Minute < 0 || TimeFields->Minute > 59 ||
TimeFields->Hour < 0 || TimeFields->Hour > 23 ||
TimeFields->Month < 1 || TimeFields->Month > 12 ||
TimeFields->Day < 1 ||
TimeFields->Day > MonthLengths
[TimeFields->Month == 2 || IsLeapYear(TimeFields->Year)]
[TimeFields->Month - 1] ||
TimeFields->Year < 1601)
return FALSE;
}
if (TimeFields->Second < 0 || TimeFields->Second > 59) {
return FALSE;
}
if (TimeFields->Minute < 0 || TimeFields->Minute > 59) {
return FALSE;
}
if (TimeFields->Hour < 0 || TimeFields->Hour > 23) {
return FALSE;
}
if (TimeFields->Month < 1 || TimeFields->Month > 12) {
return FALSE;
}
if (TimeFields->Day < 1 ||
TimeFields->Day > MonthLengths[IsLeapYear(TimeFields->Year)][TimeFields->Month - 1]) {
return FALSE;
}
if (TimeFields->Year < 1601) {
return FALSE;
}
/* now calculate a day count from the date
* First start counting years from March. This way the leap days
@ -1666,21 +1629,20 @@ XBSYSAPI EXPORTNUM(304) xbox::boolean_xt NTAPI xbox::RtlTimeFieldsToTime
month = TimeFields->Month + 1;
year = TimeFields->Year;
}
cleaps = (3 * (year / 100) + 3) / 4; /* nr of "century leap years"*/
day = (36525 * year) / 100 - cleaps + /* year * DayPerYear, corrected */
(1959 * month) / 64 + /* months * DayPerMonth */
TimeFields->Day - /* day of the month */
584817; /* zero that on 1601-01-01 */
cleaps = (3 * (year / 100) + 3) / 4; /* nr of "century leap years"*/
day = (36525 * year) / 100 - cleaps + /* year * dayperyr, corrected */
(1959 * month) / 64 + /* months * daypermonth */
TimeFields->Day - /* day of the month */
584817; /* zero that on 1601-01-01 */
/* done */
/* Convert into Time format */
Time->QuadPart = day * HOURSPERDAY;
Time->QuadPart = (Time->QuadPart + TimeFields->Hour) * MINSPERHOUR;
Time->QuadPart = (Time->QuadPart + TimeFields->Minute) * SECSPERMIN;
Time->QuadPart = (Time->QuadPart + TimeFields->Second) * MSECSPERSEC;
Time->QuadPart = (Time->QuadPart + TimeFields->Millisecond) * TICKSPERMSEC;
Time->QuadPart = (((((LONGLONG)day * HOURSPERDAY +
TimeFields->Hour) * MINSPERHOUR +
TimeFields->Minute) * SECSPERMIN +
TimeFields->Second) * 1000 +
TimeFields->Millisecond) * TICKSPERMSEC;
return TRUE;
RETURN(TRUE);
}
// ******************************************************************
@ -1692,27 +1654,36 @@ XBSYSAPI EXPORTNUM(305) xbox::void_xt NTAPI xbox::RtlTimeToTimeFields
OUT PTIME_FIELDS TimeFields
)
{
LONGLONG Days, cleaps, years, yearday, months;
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Time)
LOG_FUNC_ARG_OUT(TimeFields)
LOG_FUNC_END;
/* Extract milliseconds from time and days from milliseconds */
// NOTE: Reverse engineered native kernel uses RtlExtendedMagicDivide calls.
// Using similar code of ReactOS does not emulate native kernel's implement for
// one increment over large integer's max value.
xbox::LARGE_INTEGER MillisecondsTotal = RtlExtendedMagicDivide(*Time, Magic10000, SHIFT10000);
Days = RtlExtendedMagicDivide(MillisecondsTotal, Magic86400000, SHIFT86400000).u.LowPart;
MillisecondsTotal.QuadPart -= Days * SECSPERDAY * MSECSPERSEC;
int SecondsInDay;
long int cleaps, years, yearday, months;
long int Days;
LONGLONG _Time;
/* Extract millisecond from time and convert time into seconds */
TimeFields->Millisecond =
(cshort_xt)((Time->QuadPart % TICKSPERSEC) / TICKSPERMSEC);
_Time = Time->QuadPart / TICKSPERSEC;
/* The native version of RtlTimeToTimeFields does not take leap seconds
* into account */
/* Split the time into days and seconds within the day */
Days = (long int )(_Time / SECSPERDAY);
SecondsInDay = _Time % SECSPERDAY;
/* compute time of day */
TimeFields->Millisecond = MillisecondsTotal.u.LowPart % MSECSPERSEC;
dword_xt RemainderTime = MillisecondsTotal.u.LowPart / MSECSPERSEC;
TimeFields->Second = RemainderTime % SECSPERMIN;
RemainderTime /= SECSPERMIN;
TimeFields->Minute = RemainderTime % MINSPERHOUR;
RemainderTime /= MINSPERHOUR;
TimeFields->Hour = RemainderTime; // NOTE: Remaining hours did not received 24 hours modulo treatment.
TimeFields->Hour = (cshort_xt)(SecondsInDay / SECSPERHOUR);
SecondsInDay = SecondsInDay % SECSPERHOUR;
TimeFields->Minute = (cshort_xt)(SecondsInDay / SECSPERMIN);
TimeFields->Second = (cshort_xt)(SecondsInDay % SECSPERMIN);
/* compute day of week */
TimeFields->Weekday = (EPOCHWEEKDAY + Days) % DAYSPERWEEK;
TimeFields->Weekday = (cshort_xt)((EPOCHWEEKDAY + Days) % DAYSPERWEEK);
/* compute year, month and day of month. */
cleaps = (3 * ((4 * Days + 1227) / DAYSPERQUADRICENTENNIUM) + 3) / 4;
@ -1721,7 +1692,7 @@ XBSYSAPI EXPORTNUM(305) xbox::void_xt NTAPI xbox::RtlTimeToTimeFields
yearday = Days - (years * DAYSPERNORMALQUADRENNIUM) / 4;
months = (64 * yearday) / 1959;
/* the result is based on a year starting on March.
* To convert take 12 from January and February and
* To convert take 12 from Januari and Februari and
* increase the year by one. */
if (months < 14) {
TimeFields->Month = (USHORT)(months - 1);
@ -2054,7 +2025,7 @@ XBSYSAPI EXPORTNUM(314) xbox::ntstatus_xt NTAPI xbox::RtlUpcaseUnicodeString
if (AllocateDestinationString) {
DestinationString->MaximumLength = SourceString->Length;
DestinationString->Buffer = (wchar_xt*)ExAllocatePoolWithTag((ULONG)DestinationString->MaximumLength, 'grtS');
DestinationString->Buffer = (USHORT*)ExAllocatePoolWithTag((ULONG)DestinationString->MaximumLength, 'grtS');
if (DestinationString->Buffer == NULL) {
return X_STATUS_NO_MEMORY;
}
@ -2248,11 +2219,6 @@ XBSYSAPI EXPORTNUM(319) xbox::ulong_xt NTAPI xbox::RtlWalkFrameChain
ulong_ptr_xt NewStack = *(ulong_ptr_xt*)Stack;
ulong_xt Eip = *(ulong_ptr_xt*)(Stack + sizeof(ulong_ptr_xt));
/* Check if Eip is not below executable's dos header */
if (Eip < KiB(64)) {
break;
}
/* Check if the new pointer is above the old one and past the end */
if (!((Stack < NewStack) && (NewStack < StackEnd))) {
/* Stop searching after this entry */

View file

@ -41,19 +41,13 @@ XBSYSAPI EXPORTNUM(321) xbox::XBOX_KEY_DATA xbox::XboxEEPROMKey = { 0 };
// ******************************************************************
// * 0x0142 - XboxHardwareInfo
// ******************************************************************
// TODO: The main goal is to completely unset custom init values and have
// them set from kernel's initialization end and device classes.
// Although, device classes does not really set this value but read
// from PCI space for Gpu rev, Mcp rev, and possibility INTERNAL_USB flag.
XBSYSAPI EXPORTNUM(322) xbox::XBOX_HARDWARE_INFO xbox::XboxHardwareInfo =
{
// TODO: What exactly 0xC0000030 flags are? Might need default to null then set them later properly.
// NOTE: Will be set by src/devices/Xbox.cpp and maybe other file(s)...
.Flags = 0xC0000030, // Flags: 1=INTERNAL_USB, 2=DEVKIT, 4=MACROVISION, 8=CHIHIRO
.GpuRevision = 0xD3, // GpuRevision, byte read from NV2A first register, at 0xFD0000000 - see NV_PMC_BOOT_0
.McpRevision = 0, // NOTE: Will be set by src/devices/Xbox.cpp file.
.Unknown3 = 0, // unknown
.Unknown4 = 0 // unknown
0xC0000031, // Flags: 1=INTERNAL_USB, 2=DEVKIT, 4=MACROVISION, 8=CHIHIRO
0xA2, // GpuRevision, byte read from NV2A first register, at 0xFD0000000 - see NV_PMC_BOOT_0
0xD3, // McpRevision, Retail 1.6 - see https://github.com/JayFoxRox/xqemu-jfr/wiki/MCPX-and-bootloader
0, // unknown
0 // unknown
};
// ******************************************************************

View file

@ -99,6 +99,9 @@ bool g_bIsChihiro = false;
bool g_bIsDevKit = false;
bool g_bIsRetail = false;
// Indicates to disable/enable all interrupts when cli and sti instructions are executed
std::atomic_bool g_bEnableAllInterrupts = true;
// Set by the VMManager during initialization. Exported because it's needed in other parts of the emu
size_t g_SystemMaxMemory = 0;
@ -110,8 +113,6 @@ ULONG g_CxbxFatalErrorCode = FATAL_ERROR_NONE;
// Define function located in EmuXApi so we can call it from here
void SetupXboxDeviceTypes();
extern xbox::void_xt NTAPI system_events(xbox::PVOID arg);
void SetupPerTitleKeys()
{
// Generate per-title keys from the XBE Certificate
@ -328,6 +329,64 @@ void InitSoftwareInterrupts()
}
#endif
static xbox::void_xt NTAPI CxbxKrnlInterruptThread(xbox::PVOID param)
{
CxbxSetThreadName("CxbxKrnl Interrupts");
#if 0
InitSoftwareInterrupts();
#endif
std::mutex m;
std::unique_lock<std::mutex> lock(m);
while (true) {
for (int i = 0; i < MAX_BUS_INTERRUPT_LEVEL; i++) {
// If the interrupt is pending and connected, process it
if (HalSystemInterrupts[i].IsPending() && EmuInterruptList[i] && EmuInterruptList[i]->Connected) {
HalSystemInterrupts[i].Trigger(EmuInterruptList[i]);
}
}
g_InterruptSignal.wait(lock, []() { return g_AnyInterruptAsserted.load() && g_bEnableAllInterrupts.load(); });
g_AnyInterruptAsserted = false;
}
assert(0);
}
static void CxbxKrnlClockThread(void* pVoid)
{
LARGE_INTEGER CurrentTicks;
uint64_t Delta;
uint64_t Microseconds;
unsigned int IncrementScaling;
static uint64_t LastTicks = 0;
static uint64_t Error = 0;
static uint64_t UnaccountedMicroseconds = 0;
// This keeps track of how many us have elapsed between two cycles, so that the xbox clocks are updated
// with the proper increment (instead of blindly adding a single increment at every step)
if (LastTicks == 0) {
QueryPerformanceCounter(&CurrentTicks);
LastTicks = CurrentTicks.QuadPart;
CurrentTicks.QuadPart = 0;
}
QueryPerformanceCounter(&CurrentTicks);
Delta = CurrentTicks.QuadPart - LastTicks;
LastTicks = CurrentTicks.QuadPart;
Error += (Delta * SCALE_S_IN_US);
Microseconds = Error / HostQPCFrequency;
Error -= (Microseconds * HostQPCFrequency);
UnaccountedMicroseconds += Microseconds;
IncrementScaling = (unsigned int)(UnaccountedMicroseconds / 1000); // -> 1 ms = 1000us -> time between two xbox clock interrupts
UnaccountedMicroseconds -= (IncrementScaling * 1000);
xbox::KiClockIsr(IncrementScaling);
}
void MapThunkTable(uint32_t* kt, uint32_t* pThunkTable)
{
const bool SendDebugReports = (pThunkTable == CxbxKrnl_KernelThunkTable) && CxbxDebugger::CanReport();
@ -483,10 +542,7 @@ static void CxbxrKrnlSetupMemorySystem(int BootFlags, unsigned emulate_system, u
}
}
static bool CxbxrKrnlXbeSystemSelector(int BootFlags,
unsigned& reserved_systems,
blocks_reserved_t blocks_reserved,
HardwareModel &hardwareModel)
static bool CxbxrKrnlXbeSystemSelector(int BootFlags, unsigned& reserved_systems, blocks_reserved_t blocks_reserved)
{
unsigned int emulate_system = 0;
// Get title path :
@ -627,7 +683,6 @@ static bool CxbxrKrnlXbeSystemSelector(int BootFlags,
// Detect XBE type :
XbeType xbeType = CxbxKrnl_Xbe->GetXbeType();
EmuLogInit(LOG_LEVEL::INFO, "Auto detect: XbeType = %s", GetXbeTypeToStr(xbeType));
// Convert XBE type into corresponding system to emulate.
switch (xbeType) {
case XbeType::xtChihiro:
@ -641,10 +696,6 @@ static bool CxbxrKrnlXbeSystemSelector(int BootFlags,
break;
DEFAULT_UNREACHABLE;
}
if (std::filesystem::exists(xbeDirectory / "boot.id")) {
emulate_system = SYSTEM_CHIHIRO;
}
}
EmuLogInit(LOG_LEVEL::INFO, "Host's compatible system types: %2X", reserved_systems);
@ -666,17 +717,6 @@ static bool CxbxrKrnlXbeSystemSelector(int BootFlags,
g_bIsChihiro = (emulate_system == SYSTEM_CHIHIRO);
g_bIsDevKit = (emulate_system == SYSTEM_DEVKIT);
g_bIsRetail = (emulate_system == SYSTEM_XBOX);
if (g_bIsChihiro) {
hardwareModel = HardwareModel::Chihiro_Type1; // TODO: Make configurable to support Type-3 console.
}
else if (g_bIsDevKit) {
hardwareModel = HardwareModel::DebugKit_r1_2; // Unlikely need to make configurable.
}
// Retail (default)
else {
// Should not be configurable. Otherwise, titles compiled with newer XDK will patch older xbox kernel.
hardwareModel = HardwareModel::Revision1_6;
}
#ifdef CHIHIRO_WORK
// If this is a Chihiro title, we need to patch the init flags to disable HDD setup
@ -894,8 +934,7 @@ static bool CxbxrKrnlPrepareXbeMap()
Xbe::Header* XbeHeader,
uint32_t XbeHeaderSize,
void (*Entry)(),
int BootFlags,
HardwareModel hardwareModel
int BootFlags
);
void CxbxKrnlEmulate(unsigned int reserved_systems, blocks_reserved_t blocks_reserved)
@ -1049,8 +1088,7 @@ void CxbxKrnlEmulate(unsigned int reserved_systems, blocks_reserved_t blocks_res
// using XeLoadImage from LaunchDataPage->Header.szLaunchPath
// Now we can load the XBE :
HardwareModel hardwareModel = HardwareModel::Revision1_6; // It is configured by CxbxrKrnlXbeSystemSelector function according to console type.
if (!CxbxrKrnlXbeSystemSelector(BootFlags, reserved_systems, blocks_reserved, hardwareModel)) {
if (!CxbxrKrnlXbeSystemSelector(BootFlags, reserved_systems, blocks_reserved)) {
return;
}
@ -1078,8 +1116,7 @@ void CxbxKrnlEmulate(unsigned int reserved_systems, blocks_reserved_t blocks_res
(Xbe::Header*)CxbxKrnl_Xbe->m_Header.dwBaseAddr,
CxbxKrnl_Xbe->m_Header.dwSizeofHeaders,
(void(*)())EntryPoint,
BootFlags,
hardwareModel
BootFlags
);
}
@ -1104,10 +1141,7 @@ static void CxbxrLogDumpXbeInfo(Xbe::LibraryVersion* libVersionInfo)
EmuLogInit(LOG_LEVEL::INFO, "XBE TitleID : %s", FormatTitleId(g_pCertificate->dwTitleId).c_str());
EmuLogInit(LOG_LEVEL::INFO, "XBE TitleID (Hex) : 0x%s", titleIdHex.str().c_str());
if (CxbxKrnl_Xbe != nullptr) {
EmuLogInit(LOG_LEVEL::INFO, "XBE Version : %d.%d", CxbxKrnl_Xbe->GetDiscVersion(), CxbxKrnl_Xbe->GetPatchVersion());
}
EmuLogInit(LOG_LEVEL::INFO, "XBE Version (Hex): %08X", g_pCertificate->dwVersion);
EmuLogInit(LOG_LEVEL::INFO, "XBE Version : 1.%02d", g_pCertificate->dwVersion);
EmuLogInit(LOG_LEVEL::INFO, "XBE TitleName : %.40ls", g_pCertificate->wsTitleName);
EmuLogInit(LOG_LEVEL::INFO, "XBE Region : %s", CxbxKrnl_Xbe->GameRegionToString());
}
@ -1141,8 +1175,7 @@ static void CxbxrKrnlInitHacks()
Xbe::Header *pXbeHeader,
uint32_t dwXbeHeaderSize,
void(*Entry)(),
int BootFlags,
HardwareModel hardwareModel)
int BootFlags)
{
unsigned Host2XbStackBaseReserved = 0;
__asm mov Host2XbStackBaseReserved, esp;
@ -1166,11 +1199,11 @@ static void CxbxrKrnlInitHacks()
g_pCertificate = &CxbxKrnl_Xbe->m_Certificate;
// Initialize timer subsystem
timer_init();
Timer_Init();
// for unicode conversions
setlocale(LC_ALL, "English");
// Initialize DPC global
InitDpcData();
// Initialize time-related variables for the kernel and the timers
CxbxInitPerformanceCounters();
#ifdef _DEBUG
// PopupCustom(LOG_LEVEL::INFO, "Attach a Debugger");
// Debug child processes using https://marketplace.visualstudio.com/items?itemName=GreggMiskelly.MicrosoftChildProcessDebuggingPowerTool
@ -1308,11 +1341,10 @@ static void CxbxrKrnlInitHacks()
}
xbox::PsInitSystem();
xbox::KiInitSystem();
xbox::RtlInitSystem();
// initialize graphics
EmuLogInit(LOG_LEVEL::DEBUG, "Initializing render window.");
CxbxInitWindow();
CxbxInitWindow(true);
// Now process the boot flags to see if there are any special conditions to handle
if (BootFlags & BOOT_EJECT_PENDING) {} // TODO
@ -1348,7 +1380,7 @@ static void CxbxrKrnlInitHacks()
SetupXboxDeviceTypes();
}
InitXboxHardware(hardwareModel);
InitXboxHardware(HardwareModel::Revision1_5); // TODO : Make configurable
// Read Xbox video mode from the SMC, store it in HalBootSMCVideoMode
xbox::HalReadSMBusValue(SMBUS_ADDRESS_SYSTEM_MICRO_CONTROLLER, SMC_COMMAND_AV_PACK, FALSE, (xbox::PULONG)&xbox::HalBootSMCVideoMode);
@ -1374,29 +1406,6 @@ static void CxbxrKrnlInitHacks()
// See: https://multimedia.cx/eggs/xbox-sphinx-protocol/
ApplyMediaPatches();
// Verify that the emulator region matches the game region, if not, show a warning
// that it may not work.
if (!(g_pCertificate->dwGameRegion & EEPROM->EncryptedSettings.GameRegion))
{
auto expected = CxbxKrnl_Xbe->GameRegionToString();
auto actual = CxbxKrnl_Xbe->GameRegionToString(EEPROM->EncryptedSettings.GameRegion);
std::stringstream ss;
ss << "The loaded title is designed for region: " << expected << "\n";
ss << "However Cxbx-Reloaded is configured as: " << actual << "\n\n";
ss << "This means that you may encounter emulation issues\n\n";
ss << "You can fix this by changing your emulated Xbox region in EEPROM Settings\n\n";
ss << "Please do not submit bug reports that result from incorrect region flags\n\n";
ss << "Would you like to attempt emulation anyway?";
PopupReturn ret = PopupWarningEx(nullptr, PopupButtons::YesNo, PopupReturn::No, ss.str().c_str());
if (ret != PopupReturn::Yes)
{
CxbxrShutDown();
}
}
// Chihiro games require more patches
// The chihiro BIOS does this to bypass XAPI cache init
if (g_bIsChihiro) {
@ -1421,17 +1430,23 @@ static void CxbxrKrnlInitHacks()
#endif
EmuX86_Init();
// Start the event thread
// Create the interrupt processing thread
xbox::HANDLE hThread;
xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, system_events, xbox::zeroptr, FALSE);
// Launch the xbe
CxbxrCreateThread(&hThread, xbox::zeroptr, CxbxKrnlInterruptThread, xbox::zeroptr, FALSE);
// Start the kernel clock thread
TimerObject* KernelClockThr = Timer_Create(CxbxKrnlClockThread, nullptr, "Kernel clock thread", true);
Timer_Start(KernelClockThr, SCALE_MS_IN_NS);
xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, CxbxLaunchXbe, Entry, FALSE);
xbox::KeRaiseIrqlToDpcLevel();
while (true) {
xbox::KeWaitForDpc();
ExecuteDpcQueue();
}
EmuKeFreePcr();
__asm add esp, Host2XbStackSizeReserved;
// This will wait forever
std::condition_variable cv;
std::mutex m;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [] { return false; });
}
// REMARK: the following is useless, but PatrickvL has asked to keep it for documentation purposes
@ -1453,7 +1468,7 @@ void CxbxrKrnlSuspendThreads()
// Don't use EmuKeGetPcr because that asserts kpcr
xbox::KPCR* Pcr = reinterpret_cast<xbox::PKPCR>(__readfsdword(TIB_ArbitraryDataSlot));
// If there's nothing in list entry, skip this step.
if (!ThreadListEntry) {
return;

View file

@ -155,9 +155,7 @@ void CxbxKrnlPanic();
/*! empty function */
void CxbxKrnlNoFunc();
void InitDpcData(); // Implemented in EmuKrnlKe.cpp
bool IsDpcActive();
void ExecuteDpcQueue();
void CxbxInitPerformanceCounters(); // Implemented in EmuKrnlKe.cpp
/*! kernel thunk table */
extern uint32_t CxbxKrnl_KernelThunkTable[379];

View file

@ -57,8 +57,15 @@ static void ApplyMediaPatches()
| XBEIMAGE_MEDIA_TYPE_DVD_5_RO
| XBEIMAGE_MEDIA_TYPE_DVD_9_RO
| XBEIMAGE_MEDIA_TYPE_DVD_5_RW
| XBEIMAGE_MEDIA_TYPE_DVD_9_RW;
| XBEIMAGE_MEDIA_TYPE_DVD_9_RW
;
// Patch the XBE Header to allow running on all regions
g_pCertificate->dwGameRegion = 0
| XBEIMAGE_GAME_REGION_MANUFACTURING
| XBEIMAGE_GAME_REGION_NA
| XBEIMAGE_GAME_REGION_JAPAN
| XBEIMAGE_GAME_REGION_RESTOFWORLD
;
// Patch the XBE Security Flag
// This field is only present if the Xbe Size is >= than our Certificate Structure
// This works as our structure is large enough to fit the newer certificate size,

View file

@ -49,6 +49,11 @@ bool g_DisablePixelShaders = false;
bool g_UseAllCores = false;
bool g_SkipRdtscPatching = false;
// Delta added to host SystemTime, used in KiClockIsr and KeSetSystemTime
// This shouldn't need to be atomic, but because raising the IRQL to high lv in KeSetSystemTime doesn't really stop KiClockIsr from running,
// we need it for now to prevent reading a corrupted value while KeSetSystemTime is in the middle of updating it
std::atomic_int64_t HostSystemTimeDelta(0);
// Static Function(s)
static int ExitException(LPEXCEPTION_POINTERS e);

View file

@ -68,6 +68,9 @@ extern HWND g_hEmuWindow;
extern HANDLE g_CurrentProcessHandle; // Set in CxbxKrnlMain
// Delta added to host SystemTime, used in KiClockIsr and KeSetSystemTime
extern std::atomic_int64_t HostSystemTimeDelta;
typedef struct DUMMY_KERNEL
{
IMAGE_DOS_HEADER DosHeader;

View file

@ -40,6 +40,7 @@
#include <ntstatus.h>
#pragma warning(default:4005)
#include "Logging.h"
#include "common/util/strConverter.hpp" // utf16_to_ascii
#include "common/util/cliConfig.hpp"
#include "common/CxbxDebugger.h"
#include "EmuShared.h"
@ -789,21 +790,20 @@ std::string CxbxConvertXboxToHostPath(const std::string_view XboxDevicePath)
std::wstring wXbePath;
// We pretend to come from NtCreateFile to force symbolic link resolution
CxbxConvertFilePath(XboxDevicePath.data(), wXbePath, &rootDirectoryHandle, "NtCreateFile");
std::filesystem::path XbePath;
// Convert Wide String as returned by above to a string, for XbePath
std::string XbePath = utf16_to_ascii(wXbePath.c_str());
// If the rootDirectoryHandle is not null, we have a relative path
// We need to prepend the path of the root directory to get a full DOS path
if (rootDirectoryHandle != nullptr) {
WCHAR directoryPathBuffer[MAX_PATH];
GetFinalPathNameByHandleW(rootDirectoryHandle, directoryPathBuffer, MAX_PATH, VOLUME_NAME_DOS);
XbePath = directoryPathBuffer;
XbePath /= wXbePath;
}
else {
XbePath = wXbePath;
std::string directoryPath = utf16_to_ascii(directoryPathBuffer);
XbePath = directoryPath + std::string("\\") + XbePath;
}
return XbePath.string();
return XbePath;
}
int CxbxRegisterDeviceHostPath(const std::string_view XboxDevicePath, std::string HostDevicePath, bool IsFile, std::size_t size)

View file

@ -26,7 +26,6 @@
#include <core\kernel\exports\xboxkrnl.h>
#include <filesystem>
#include "core\kernel\init\CxbxKrnl.h"
#include <vector>
#include <cstdio>

View file

@ -91,13 +91,10 @@ uint8_t SMCDevice::ReadByte(uint8_t command)
// See https://xboxdevwiki.net/PIC#PIC_version_string
switch (m_revision) {
case SCMRevision::P01: buffer[1] = "P01"[m_PICVersionStringIndex]; break;
case SCMRevision::P05: buffer[1] = "P05"[m_PICVersionStringIndex]; break;
case SCMRevision::P11: buffer[1] = "P11"[m_PICVersionStringIndex]; break;
case SCMRevision::P2L: buffer[1] = "P2L"[m_PICVersionStringIndex]; break;
case SCMRevision::P2L: buffer[1] = "P05"[m_PICVersionStringIndex]; break; // ??
case SCMRevision::D01: buffer[1] = "DXB"[m_PICVersionStringIndex]; break;
case SCMRevision::D05: buffer[1] = "D05"[m_PICVersionStringIndex]; break; // ??
case SCMRevision::B11: buffer[1] = "B11"[m_PICVersionStringIndex]; break; // ??
default: CxbxrAbort("Unknown PIC revision: %d", m_revision);
// default: UNREACHABLE(m_revision);
}
m_PICVersionStringIndex = (m_PICVersionStringIndex + 1) % 3;

View file

@ -93,14 +93,10 @@
typedef enum {
// https://xboxdevwiki.net/System_Management_Controller
// https://xboxdevwiki.net/Xboxen_Info
P01,
P05,
P11,
P2L,
D01, // Seen in a debug kit
D05, // Seen in a earlier model chihiro
B11,
} SCMRevision;
class SMCDevice : public SMDevice {

View file

@ -25,14 +25,9 @@
// *
// ******************************************************************
#define LOG_PREFIX CXBXR_MODULE::XBOX
#include "Xbox.h" // For HardwareModel
#include "common\xbe\Xbe.h" // Without this HLEIntercept complains about some undefined xbe variables
#include "core\kernel\common\xbox.h"
#include "cxbxr.hpp"
#include "core\hle\Intercept.hpp"
#include "EmuShared.h"
PCIBus* g_PCIBus;
SMBus* g_SMBus;
@ -43,86 +38,69 @@ NVNetDevice* g_NVNet;
NV2ADevice* g_NV2A;
ADM1032Device* g_ADM1032;
USBDevice* g_USB0;
MediaBoard* g_MediaBoard;
MCPXRevision MCPXRevisionFromHardwareModel(HardwareModel hardwareModel)
{
// https://xboxdevwiki.net/Xboxen_Info
switch (GET_HW_CONSOLE(hardwareModel)) {
case Retail:
switch (hardwareModel) {
case Revision1_0:
case Revision1_1:
case Revision1_2:
case Revision1_3:
case Revision1_4:
case Revision1_5:
case Revision1_6:
return MCPXRevision::MCPX_X3;
case DebugKit:
case Chihiro:
// EmuLog(LOG_LEVEL::WARNING, "Guessing MCPXVersion");
return MCPXRevision::MCPX_X2;
default:
CxbxrAbort("MCPXRevisionFromHardwareModel: Unknown conversion for hardware model (0x%02X)", hardwareModel);
// UNREACHABLE(hardwareModel);
return MCPXRevision::MCPX_X3;
}
}
SCMRevision SCMRevisionFromHardwareModel(HardwareModel hardwareModel)
{
// https://xboxdevwiki.net/Xboxen_Info
switch (hardwareModel) {
case Revision1_0:
return SCMRevision::P01;
return SCMRevision::P01; // Our SCM returns PIC version string "P01"
case Revision1_1:
return SCMRevision::P05;
case Revision1_2:
case Revision1_3:
case Revision1_4:
return SCMRevision::P11;
case Revision1_5:
case Revision1_6:
return SCMRevision::P2L;
// EmuLog(LOG_LEVEL::WARNING, "Guessing SCMRevision");
return SCMRevision::P2L; // Assumption; Our SCM returns PIC version string "P05"
case DebugKit:
return SCMRevision::D01;
case Chihiro_Type1:
return SCMRevision::D05;
case DebugKit_r1_2:
case Chihiro_Type3:
return SCMRevision::B11;
return SCMRevision::D01; // Our SCM returns PIC version string "DXB"
default:
CxbxrAbort("SCMRevisionFromHardwareModel: Unknown conversion for hardware model (0x%02X)", hardwareModel);
// UNREACHABLE(hardwareModel);
return SCMRevision::P2L;
}
}
TVEncoder TVEncoderFromHardwareModel(HardwareModel hardwareModel)
{
// https://xboxdevwiki.net/Xboxen_Info
// LukeUsher : My debug kit and at least most of them (maybe all?)
// are equivalent to v1.0 and have Conexant encoders.
switch (GET_HW_REVISION(hardwareModel)) {
switch (hardwareModel) {
case Revision1_0:
case Revision1_1:
case Revision1_2:
case Revision1_3:
return TVEncoder::Conexant;
case Revision1_4:
case Revision1_5: // Assumption
return TVEncoder::Focus;
case Revision1_5:
return TVEncoder::Focus; // Assumption
case Revision1_6:
return TVEncoder::XCalibur;
default:
CxbxrAbort("TVEncoderFromHardwareModel: Unknown conversion for hardware model (0x%02X)", hardwareModel);
}
}
xbox::uchar_xt MCP_PCIRevisionFromHardwareModel(HardwareModel hardwareModel)
{
// https://xboxdevwiki.net/Xboxen_Info
switch (GET_HW_REVISION(hardwareModel)) {
case Revision1_0:
return 0xB2;
case Revision1_1:
case Revision1_2:
case Revision1_3:
case Revision1_4:
case Revision1_5: // Assumption
return 0xD4;
case Revision1_6:
return 0xD5;
default:
CxbxrAbort("MCP_PCIRevisionFromHardwareModel: Unknown conversion for hardware model (0x%02X)", hardwareModel);
case DebugKit:
// LukeUsher : My debug kit and at least most of them (maybe all?)
// are equivalent to v1.0 and have Conexant encoders.
return TVEncoder::Conexant;
default:
// UNREACHABLE(hardwareModel);
return TVEncoder::Focus;
}
}
@ -133,43 +111,22 @@ void InitXboxHardware(HardwareModel hardwareModel)
SCMRevision smc_revision = SCMRevisionFromHardwareModel(hardwareModel);
TVEncoder tv_encoder = TVEncoderFromHardwareModel(hardwareModel);
// Only Xbox 1.0 has usb daughterboard supplied, later revisions are integrated onto motherboard.
if (GET_HW_REVISION(hardwareModel) == HardwareModel::Revision1_0) {
xbox::XboxHardwareInfo.Flags |= XBOX_HW_FLAG_INTERNAL_USB_HUB;
}
// Set the special type of consoles according to xbox kernel designed respectively.
if (IS_DEVKIT(hardwareModel)) {
xbox::XboxHardwareInfo.Flags |= XBOX_HW_FLAG_DEVKIT_KERNEL;
}
else if (IS_CHIHIRO(hardwareModel)) {
xbox::XboxHardwareInfo.Flags |= XBOX_HW_FLAG_ARCADE;
}
xbox::XboxHardwareInfo.McpRevision = MCP_PCIRevisionFromHardwareModel(hardwareModel);
// Create busses
g_PCIBus = new PCIBus();
g_SMBus = new SMBus();
// Create devices
g_MCPX = new MCPXDevice(mcpx_revision);
// TODO: For Chihiro, different games modes require different DIP switch settings
// Chihiro FilterBoard dip-switches 6,7,8 change this value!
g_SMC = new SMCDevice(smc_revision, IS_CHIHIRO(hardwareModel) ? 0 : 1); // 0 = AV_PACK_SCART, 1 = AV_PACK_HDTV. Chihiro doesn't support HDTV!
// SMC uses different AV_PACK values than the Kernel
// See https://xboxdevwiki.net/PIC#The_AV_Pack
g_SMC = new SMCDevice(smc_revision, g_bIsChihiro ? 6 : 1); // 6 = AV_PACK_STANDARD, 1 = AV_PACK_HDTV. Chihiro doesn't support HDTV!
// SMC uses different AV_PACK values than the Kernel
// See https://xboxdevwiki.net/PIC#The_AV_Pack
g_EEPROM = new EEPROMDevice();
g_NVNet = new NVNetDevice();
g_NV2A = new NV2ADevice();
g_ADM1032 = new ADM1032Device();
g_USB0 = new USBDevice();
if (IS_CHIHIRO(hardwareModel)) {
g_MediaBoard = new MediaBoard();
char MediaBoardMountPath[xbox::max_path];
g_EmuShared->GetTitleMountPath(MediaBoardMountPath);
g_MediaBoard->SetMountPath(MediaBoardMountPath);
if (bLLE_USB) {
g_USB0 = new USBDevice();
}
// Connect devices to SM bus
@ -199,12 +156,14 @@ void InitXboxHardware(HardwareModel hardwareModel)
//g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(5, 0)), g_NVAPU);
//g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(6, 0)), g_AC97);
g_PCIBus->ConnectDevice(PCI_DEVID(1, PCI_DEVFN(0, 0)), g_NV2A);
// ergo720: according to some research done by LukeUsher, only Xbox Alpha Kits have a two HCs configuration. This seems to also be confirmed by the xboxdevwiki,
// which states that it has a xircom PGPCI2(OPTI 82C861) 2 USB port PCI card -> 2 ports, not 4. Finally, I disassembled various xbe's and discovered that the number
// of ports per HC is hardcoded as 4 in the driver instead of being detected at runtime by reading the HcRhDescriptorA register and so a game would have to be
// recompiled to support 2 HCs, which further confirms the point. Because we are not going to emulate an Alpha Kit, we can simply ignore the USB1 device.
if (bLLE_USB) {
// ergo720: according to some research done by LukeUsher, only Xbox Alpha Kits have a two HCs configuration. This seems to also be confirmed by the xboxdevwiki,
// which states that it has a xircom PGPCI2(OPTI 82C861) 2 USB port PCI card -> 2 ports, not 4. Finally, I disassembled various xbe's and discovered that the number
// of ports per HC is hardcoded as 4 in the driver instead of being detected at runtime by reading the HcRhDescriptorA register and so a game would have to be
// recompiled to support 2 HCs, which further confirms the point. Because we are not going to emulate an Alpha Kit, we can simply ignore the USB1 device.
g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(2, 0)), g_USB0);
g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(2, 0)), g_USB0);
}
// TODO : Handle other SMBUS Addresses, like PIC_ADDRESS, XCALIBUR_ADDRESS
// Resources : http://pablot.com/misc/fancontroller.cpp

View file

@ -35,7 +35,6 @@
#include "ADM1032Device.h" // For ADM1032
#include "devices\video\nv2a.h" // For NV2ADevice
#include "Usb\USBDevice.h" // For USBDevice
#include "chihiro\MediaBoard.h"
#define SMBUS_ADDRESS_MCPX 0x10 // = Write; Read = 0x11
#define SMBUS_ADDRESS_TV_ENCODER 0x88 // = Write; Read = 0x89
@ -54,21 +53,9 @@ typedef enum {
Revision1_4,
Revision1_5,
Revision1_6,
Retail = 0x00,
// We don't need include revison 1.0 to 1.6 here, use above revision range instead.
DebugKit = 0x10, // TODO: Since there are 1.0/1.1/1.2 revisions. For now, let's go with 1.2 by default.
DebugKit_r1_2 = DebugKit | Revision1_2,
Chihiro = 0x20,
Chihiro_Type1 = Chihiro | Revision1_1,
Chihiro_Type3 = Chihiro | Revision1_2, // TODO: Need verify on Chihiro hw, it is currently base on (B11) Debug Kit list.
DebugKit
} HardwareModel;
#define GET_HW_REVISION(hardwareModel) (hardwareModel & 0x0F)
#define GET_HW_CONSOLE(hardwareModel) (hardwareModel & 0xF0)
#define IS_RETAIL(hardwareModel) (GET_HW_CONSOLE(hardwareModel) == Retail)
#define IS_DEVKIT(hardwareModel) (GET_HW_CONSOLE(hardwareModel) == DebugKit)
#define IS_CHIHIRO(hardwareModel) (GET_HW_CONSOLE(hardwareModel) == Chihiro)
typedef enum { // TODO : Move to it's own file
// https://xboxdevwiki.net/Hardware_Revisions#Video_encoder
Conexant,
@ -84,6 +71,5 @@ extern EEPROMDevice* g_EEPROM;
extern NVNetDevice* g_NVNet;
extern NV2ADevice* g_NV2A;
extern USBDevice* g_USB0;
extern MediaBoard* g_MediaBoard;
extern void InitXboxHardware(HardwareModel hardwareModel);

View file

@ -1,443 +0,0 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2019 Luke Usher
// *
// * All rights reserved
// *
// ******************************************************************
#include "JvsIo.h"
#include <cstdio>
#include <string>
JvsIo* g_pJvsIo;
//#define DEBUG_JVS_PACKETS
#include <vector>
#include <Windows.h>
// We will emulate SEGA 837-13551 IO Board
JvsIo::JvsIo(uint8_t* sense)
{
pSense = sense;
// Version info BCD Format: X.X
CommandFormatRevision = 0x11;
JvsVersion = 0x20;
CommunicationVersion = 0x10;
BoardID = "SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551";
}
void JvsIo::Update()
{
// Handle coin input
static bool previousCoinButtonsState = false;
bool currentCoinButtonState = GetAsyncKeyState('5');
if (currentCoinButtonState && !previousCoinButtonsState) {
Inputs.coins[0].coins += 1;
}
previousCoinButtonsState = currentCoinButtonState;
// TODO: Update Jvs inputs based on user configuration
// For now, hardcode the inputs for the game we are currently testing (Ollie King)
Inputs.switches.player[0].start = GetAsyncKeyState('1'); // Start
Inputs.analog[1].value = GetAsyncKeyState(VK_LEFT) ? 0x9000 : (GetAsyncKeyState(VK_RIGHT) ? 0x7000 : 0x8000); // Board Swing
Inputs.switches.player[0].up = GetAsyncKeyState(VK_UP); // Board Front
Inputs.switches.player[0].down = GetAsyncKeyState(VK_DOWN); // Board Rear
Inputs.switches.player[0].button[0] = GetAsyncKeyState('A'); // Left Button
Inputs.switches.player[0].button[1] = GetAsyncKeyState('S'); // Right Button
}
uint8_t JvsIo::GetDeviceId()
{
return BroadcastPacket ? 0x00 : DeviceId;
}
int JvsIo::Jvs_Command_F0_Reset(uint8_t* data)
{
uint8_t ensure_reset = data[1];
if (ensure_reset == 0xD9) {
// Set sense to 3 (2.5v) to instruct the baseboard we're ready.
*pSense = 3;
ResponseBuffer.push_back(ReportCode::Handled); // Note : Without this, Chihiro software stops sending packets (but JVS V3 doesn't send this?)
DeviceId = 0;
}
#if 0 // TODO : Is the following required?
else {
ResponseBuffer.push_back(ReportCode::InvalidParameter);
}
#endif
#if 0 // TODO : Is the following required?
// Detect a consecutive reset
if (data[2] == 0xF0) {
// TODO : Probably ensure the second reset too : if (data[3] == 0xD9) {
// TODO : Handle two consecutive reset's here?
return 3;
}
#endif
return 1;
}
int JvsIo::Jvs_Command_F1_SetDeviceId(uint8_t* data)
{
// Set Address
DeviceId = data[1];
*pSense = 0; // Set sense to 0v
ResponseBuffer.push_back(ReportCode::Handled);
return 1;
}
int JvsIo::Jvs_Command_10_GetBoardId()
{
// Get Board ID
ResponseBuffer.push_back(ReportCode::Handled);
for (char& c : BoardID) {
ResponseBuffer.push_back(c);
}
return 0;
}
int JvsIo::Jvs_Command_11_GetCommandFormat()
{
ResponseBuffer.push_back(ReportCode::Handled);
ResponseBuffer.push_back(CommandFormatRevision);
return 0;
}
int JvsIo::Jvs_Command_12_GetJvsRevision()
{
ResponseBuffer.push_back(ReportCode::Handled);
ResponseBuffer.push_back(JvsVersion);
return 0;
}
int JvsIo::Jvs_Command_13_GetCommunicationVersion()
{
ResponseBuffer.push_back(ReportCode::Handled);
ResponseBuffer.push_back(CommunicationVersion);
return 0;
}
int JvsIo::Jvs_Command_14_GetCapabilities()
{
ResponseBuffer.push_back(ReportCode::Handled);
// Capabilities list (4 bytes each)
// Input capabilities
ResponseBuffer.push_back(CapabilityCode::PlayerSwitchButtonSets);
ResponseBuffer.push_back(JVS_MAX_PLAYERS); // number of players
ResponseBuffer.push_back(13); // 13 button switches per player
ResponseBuffer.push_back(0);
ResponseBuffer.push_back(CapabilityCode::CoinSlots);
ResponseBuffer.push_back(JVS_MAX_COINS); // number of coin slots
ResponseBuffer.push_back(0);
ResponseBuffer.push_back(0);
ResponseBuffer.push_back(CapabilityCode::AnalogInputs);
ResponseBuffer.push_back(JVS_MAX_ANALOG); // number of analog input channels
ResponseBuffer.push_back(16); // 16 bits per analog input channel
ResponseBuffer.push_back(0);
// Output capabilities
ResponseBuffer.push_back(CapabilityCode::GeneralPurposeOutputs);
ResponseBuffer.push_back(6); // number of outputs
ResponseBuffer.push_back(0);
ResponseBuffer.push_back(0);
ResponseBuffer.push_back(CapabilityCode::EndOfCapabilities);
return 0;
}
int JvsIo::Jvs_Command_20_ReadSwitchInputs(uint8_t* data)
{
static jvs_switch_player_inputs_t default_switch_player_input;
uint8_t nr_switch_players = data[1];
uint8_t bytesPerSwitchPlayerInput = data[2];
ResponseBuffer.push_back(ReportCode::Handled);
ResponseBuffer.push_back(Inputs.switches.system.GetByte0());
for (int i = 0; i < nr_switch_players; i++) {
for (int j = 0; j < bytesPerSwitchPlayerInput; j++) {
// If a title asks for more switch player inputs than we support, pad with dummy data
jvs_switch_player_inputs_t &switch_player_input = (i >= JVS_MAX_PLAYERS) ? default_switch_player_input : Inputs.switches.player[i];
uint8_t value
= (j == 0) ? switch_player_input.GetByte0()
: (j == 1) ? switch_player_input.GetByte1()
: 0; // Pad any remaining bytes with 0, as we don't have that many inputs available
ResponseBuffer.push_back(value);
}
}
return 2;
}
int JvsIo::Jvs_Command_21_ReadCoinInputs(uint8_t* data)
{
static jvs_coin_slots_t default_coin_slot;
uint8_t nr_coin_slots = data[1];
ResponseBuffer.push_back(ReportCode::Handled);
for (int i = 0; i < nr_coin_slots; i++) {
const uint8_t bytesPerCoinSlot = 2;
for (int j = 0; j < bytesPerCoinSlot; j++) {
// If a title asks for more coin slots than we support, pad with dummy data
jvs_coin_slots_t &coin_slot = (i >= JVS_MAX_COINS) ? default_coin_slot : Inputs.coins[i];
uint8_t value
= (j == 0) ? coin_slot.GetByte0()
: (j == 1) ? coin_slot.GetByte1()
: 0; // Pad any remaining bytes with 0, as we don't have that many inputs available
ResponseBuffer.push_back(value);
}
}
return 1;
}
int JvsIo::Jvs_Command_22_ReadAnalogInputs(uint8_t* data)
{
static jvs_analog_input_t default_analog;
uint8_t nr_analog_inputs = data[1];
ResponseBuffer.push_back(ReportCode::Handled);
for (int i = 0; i < nr_analog_inputs; i++) {
const uint8_t bytesPerAnalogInput = 2;
for (int j = 0; j < bytesPerAnalogInput; j++) {
// If a title asks for more analog input than we support, pad with dummy data
jvs_analog_input_t &analog_input = (i >= JVS_MAX_ANALOG) ? default_analog : Inputs.analog[i];
uint8_t value
= (j == 0) ? analog_input.GetByte0()
: (j == 1) ? analog_input.GetByte1()
: 0; // Pad any remaining bytes with 0, as we don't have that many inputs available
ResponseBuffer.push_back(value);
}
}
return 1;
}
int JvsIo::Jvs_Command_32_GeneralPurposeOutput(uint8_t* data)
{
uint8_t banks = data[1];
ResponseBuffer.push_back(ReportCode::Handled);
// TODO: Handle output
// Input data size is 1 byte indicating the number of banks, followed by one byte per bank
return 1 + banks;
}
uint8_t JvsIo::GetByte(uint8_t* &buffer)
{
uint8_t value = *buffer++;
#ifdef DEBUG_JVS_PACKETS
printf(" %02X", value);
#endif
return value;
}
uint8_t JvsIo::GetEscapedByte(uint8_t* &buffer)
{
uint8_t value = GetByte(buffer);
// Special case: 0xD0 is an exception byte that actually returns the next byte + 1
if (value == ESCAPE_BYTE) {
value = GetByte(buffer) + 1;
}
return value;
}
void JvsIo::HandlePacket(jvs_packet_header_t* header, std::vector<uint8_t>& packet)
{
// It's possible for a JVS packet to contain multiple commands, so we must iterate through it
ResponseBuffer.push_back(StatusCode::StatusOkay); // Assume we'll handle the command just fine
for (size_t i = 0; i < packet.size(); i++) {
BroadcastPacket = packet[i] >= 0xF0; // Set a flag when broadcast packet
uint8_t* command_data = &packet[i];
switch (packet[i]) {
// Broadcast Commands
case 0xF0: i += Jvs_Command_F0_Reset(command_data); break;
case 0xF1: i += Jvs_Command_F1_SetDeviceId(command_data); break;
// Init Commands
case 0x10: i += Jvs_Command_10_GetBoardId(); break;
case 0x11: i += Jvs_Command_11_GetCommandFormat(); break;
case 0x12: i += Jvs_Command_12_GetJvsRevision(); break;
case 0x13: i += Jvs_Command_13_GetCommunicationVersion(); break;
case 0x14: i += Jvs_Command_14_GetCapabilities(); break;
case 0x20: i += Jvs_Command_20_ReadSwitchInputs(command_data); break;
case 0x21: i += Jvs_Command_21_ReadCoinInputs(command_data); break;
case 0x22: i += Jvs_Command_22_ReadAnalogInputs(command_data); break;
case 0x32: i += Jvs_Command_32_GeneralPurposeOutput(command_data); break;
default:
// Overwrite the verly-optimistic StatusCode::StatusOkay with Status::Unsupported command
// Don't process any further commands. Existing processed commands must still return their responses.
ResponseBuffer[0] = StatusCode::UnsupportedCommand;
printf("JvsIo::HandlePacket: Unhandled Command %02X\n", packet[i]);
return;
}
}
}
size_t JvsIo::SendPacket(uint8_t* buffer)
{
// Remember where the buffer started (so we can calculate the number of bytes we've handled)
uint8_t* buffer_start = buffer;
// Scan the packet header
jvs_packet_header_t header;
// First, read the sync byte
#ifdef DEBUG_JVS_PACKETS
printf("JvsIo::SendPacket:");
#endif
header.sync = GetByte(buffer); // Do not unescape the sync-byte!
if (header.sync != SYNC_BYTE) {
#ifdef DEBUG_JVS_PACKETS
printf(" [Missing SYNC_BYTE!]\n");
#endif
// If it's wrong, return we've processed (actually, skipped) one byte
return 1;
}
// Read the target and count bytes
header.target = GetEscapedByte(buffer);
header.count = GetEscapedByte(buffer);
// Calculate the checksum
uint8_t actual_checksum = header.target + header.count;
// Decode the payload data
std::vector<uint8_t> packet;
for (int i = 0; i < header.count - 1; i++) { // Note : -1 to avoid adding the checksum byte to the packet
uint8_t value = GetEscapedByte(buffer);
packet.push_back(value);
actual_checksum += value;
}
// Read the checksum from the last byte
uint8_t packet_checksum = GetEscapedByte(buffer);
#ifdef DEBUG_JVS_PACKETS
printf("\n");
#endif
// Verify checksum - skip packet if invalid
ResponseBuffer.clear();
if (packet_checksum != actual_checksum) {
ResponseBuffer.push_back(StatusCode::ChecksumError);
} else {
// If the packet was intended for us, we need to handle it
if (header.target == TARGET_BROADCAST || header.target == DeviceId) {
HandlePacket(&header, packet);
}
}
// Calculate and return the total packet size including header
size_t total_packet_size = buffer - buffer_start;
return total_packet_size;
}
void JvsIo::SendByte(uint8_t* &buffer, uint8_t value)
{
*buffer++ = value;
}
void JvsIo::SendEscapedByte(uint8_t* &buffer, uint8_t value)
{
// Special case: Send an exception byte followed by value - 1
if (value == SYNC_BYTE || value == ESCAPE_BYTE) {
SendByte(buffer, ESCAPE_BYTE);
value--;
}
SendByte(buffer, value);
}
size_t JvsIo::ReceivePacket(uint8_t* buffer)
{
if (ResponseBuffer.empty()) {
return 0;
}
// Build a JVS response packet containing the payload
jvs_packet_header_t header;
header.sync = SYNC_BYTE;
header.target = TARGET_MASTER_DEVICE;
header.count = (uint8_t)ResponseBuffer.size() + 1; // Set data size to payload + 1 checksum byte
// TODO : What if count overflows (meaning : responses are bigger than 255 bytes); Should we split it over multiple packets??
// Remember where the buffer started (so we can calculate the number of bytes we've send)
uint8_t* buffer_start = buffer;
// Send the header bytes
SendByte(buffer, header.sync); // Do not escape the sync byte!
SendEscapedByte(buffer, header.target);
SendEscapedByte(buffer, header.count);
// Calculate the checksum
uint8_t packet_checksum = header.target + header.count;
// Encode the payload data
for (size_t i = 0; i < ResponseBuffer.size(); i++) {
uint8_t value = ResponseBuffer[i];
SendEscapedByte(buffer, value);
packet_checksum += value;
}
// Write the checksum to the last byte
SendEscapedByte(buffer, packet_checksum);
ResponseBuffer.clear();
// Calculate an return the total packet size including header
size_t total_packet_size = buffer - buffer_start;
#ifdef DEBUG_JVS_PACKETS
printf("JvsIo::ReceivePacket:");
for (size_t i = 0; i < total_packet_size; i++) {
printf(" %02X", buffer_start[i]);
}
printf("\n");
#endif
return total_packet_size;
}

View file

@ -1,215 +0,0 @@
// ******************************************************************
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2019 Luke Usher
// *
// * All rights reserved
// *
// ******************************************************************
#ifndef JVSIO_H
#define JVSIO_H
#include <cstdint>
#include <vector>
#include <string>
typedef struct {
uint8_t sync;
uint8_t target;
uint8_t count;
} jvs_packet_header_t;
#define JVS_MAX_PLAYERS (2)
#define JVS_MAX_ANALOG (8)
#define JVS_MAX_COINS (JVS_MAX_PLAYERS)
typedef struct _jvs_switch_player_inputs_t {
bool start = false;
bool service = false;
bool up = false;
bool down = false;
bool left = false;
bool right = false;
bool button[7] = { false };
uint8_t GetByte0() {
uint8_t value = 0;
value |= start ? 1 << 7 : 0;
value |= service ? 1 << 6 : 0;
value |= up ? 1 << 5 : 0;
value |= down ? 1 << 4 : 0;
value |= left ? 1 << 3 : 0;
value |= right ? 1 << 2 : 0;
value |= button[0] ? 1 << 1 : 0;
value |= button[1] ? 1 << 0 : 0;
return value;
}
uint8_t GetByte1() {
uint8_t value = 0;
value |= button[2] ? 1 << 7 : 0;
value |= button[3] ? 1 << 6 : 0;
value |= button[4] ? 1 << 5 : 0;
value |= button[5] ? 1 << 4 : 0;
value |= button[6] ? 1 << 3 : 0;
return value;
}
} jvs_switch_player_inputs_t;
typedef struct _jvs_switch_system_inputs_t {
bool test = false;
bool tilt1 = false;
bool tilt2 = false;
bool tilt3 = false;
uint8_t GetByte0() {
uint8_t value = 0;
value |= test ? 1 << 7 : 0;
value |= tilt1 ? 1 << 6 : 0;
value |= tilt2 ? 1 << 5 : 0;
value |= tilt3 ? 1 << 4 : 0;
return value;
}
} jvs_switch_system_inputs_t;
typedef struct {
jvs_switch_system_inputs_t system;
jvs_switch_player_inputs_t player[JVS_MAX_PLAYERS];
} jvs_switch_inputs_t;
typedef struct _jvs_analog_input_t {
uint16_t value = 0x8000;
uint8_t GetByte0() {
return (value >> 8) & 0xFF;
}
uint8_t GetByte1() {
return value & 0xFF;
}
} jvs_analog_input_t;
typedef struct _jvs_coin_slots_t {
uint16_t coins = 0;
uint8_t status = 0;
uint8_t GetByte0() {
uint8_t value = 0;
value |= (status << 6) & 0xC0;
value |= (coins >> 8) & 0x3F;
return value;
}
uint8_t GetByte1() {
return coins & 0xFF;
}
} jvs_coin_slots_t;
typedef struct {
jvs_switch_inputs_t switches;
jvs_analog_input_t analog[JVS_MAX_ANALOG];
jvs_coin_slots_t coins[JVS_MAX_COINS];
} jvs_input_states_t;
class JvsIo
{
public:
JvsIo(uint8_t *sense);
size_t SendPacket(uint8_t *buffer);
size_t ReceivePacket(uint8_t *buffer);
uint8_t GetDeviceId();
void Update();
private:
const uint8_t SYNC_BYTE = 0xE0;
const uint8_t ESCAPE_BYTE = 0xD0;
const uint8_t TARGET_MASTER_DEVICE = 0x00;
const uint8_t TARGET_BROADCAST = 0xFF;
uint8_t GetByte(uint8_t *&buffer);
uint8_t GetEscapedByte(uint8_t *&buffer);
void HandlePacket(jvs_packet_header_t *header, std::vector<uint8_t> &packet);
void SendByte(uint8_t *&buffer, uint8_t value);
void SendEscapedByte(uint8_t *&buffer, uint8_t value);
enum StatusCode {
StatusOkay = 1,
UnsupportedCommand = 2,
ChecksumError = 3,
AcknowledgeOverflow = 4,
};
enum ReportCode {
Handled = 1,
NotEnoughParameters = 2,
InvalidParameter = 3,
Busy = 4,
};
enum CapabilityCode {
EndOfCapabilities = 0x00,
// Input capabilities :
PlayerSwitchButtonSets = 0x01,
CoinSlots = 0x02,
AnalogInputs = 0x03,
RotaryInputs = 0x04, // Params : JVS_MAX_ROTARY, 0, 0
KeycodeInputs = 0x05,
ScreenPointerInputs = 0x06, // Params : Xbits, Ybits, JVS_MAX_POINTERS
SwitchInputs = 0x07,
// Output capabilities :
CardSystem = 0x10, // Params : JVS_MAX_CARDS, 0, 0
MedalHopper = 0x11, // Params : max?, 0, 0
GeneralPurposeOutputs = 0x12, // Params : number of outputs, 0, 0
AnalogOutput = 0x13, // Params : channels, 0, 0
CharacterOutput = 0x14, // Params : width, height, type
BackupData = 0x15,
};
// Commands
// These return the additional param bytes used
int Jvs_Command_F0_Reset(uint8_t *data);
int Jvs_Command_F1_SetDeviceId(uint8_t *data);
int Jvs_Command_10_GetBoardId();
int Jvs_Command_11_GetCommandFormat();
int Jvs_Command_12_GetJvsRevision();
int Jvs_Command_13_GetCommunicationVersion();
int Jvs_Command_14_GetCapabilities();
int Jvs_Command_20_ReadSwitchInputs(uint8_t *data);
int Jvs_Command_21_ReadCoinInputs(uint8_t *data);
int Jvs_Command_22_ReadAnalogInputs(uint8_t *data);
int Jvs_Command_32_GeneralPurposeOutput(uint8_t *data);
bool BroadcastPacket; // Set when the last command was a broadcast
uint8_t *pSense = nullptr; // Pointer to Sense line
uint8_t DeviceId = 0; // Device ID assigned by running title
std::vector<uint8_t> ResponseBuffer; // Command Response
// Device info
uint8_t CommandFormatRevision;
uint8_t JvsVersion;
uint8_t CommunicationVersion;
std::string BoardID;
jvs_input_states_t Inputs;
};
extern JvsIo *g_pJvsIo;
#endif

View file

@ -1,161 +0,0 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// * src->devices->chihiro->MediaBoard.cpp
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2019 Luke Usher
// *
// * All rights reserved
// *
// ******************************************************************
#include "MediaBoard.h"
#include <cstdio>
#include <string>
#define _XBOXKRNL_DEFEXTRN_
#define LOG_PREFIX CXBXR_MODULE::JVS // TODO: XBAM
#include <core\kernel\exports\xboxkrnl.h>
#include "core\kernel\init\\CxbxKrnl.h"
#include "core\kernel\exports\EmuKrnl.h" // for HalSystemInterrupts
chihiro_bootid &MediaBoard::GetBootId()
{
return BootID;
}
void MediaBoard::SetMountPath(std::string path)
{
m_MountPath = path;
// Load Boot.id from file
FILE* bootidFile = fopen((path+"/boot.id").c_str(), "rb");
if (bootidFile == nullptr) {
CxbxrAbort("Could not open Chihiro boot.id");
}
fread(&BootID, 1, sizeof(chihiro_bootid), bootidFile);
fclose(bootidFile);
}
uint32_t MediaBoard::LpcRead(uint32_t addr, int size)
{
switch (addr) {
case 0x401E: return 0x0317; // Firmware Version Number
case 0x4020: return 0x00A0; // XBAM String (SEGABOOT reports Media Board is not present if these values change)
case 0x4022: return 0x4258; // Continued
case 0x4024: return 0x4D41; // Continued
// TODO: Find a way to make the switch between Type-1 and Type-3 (internal value holder maybe?)
case 0x40F0: return 0x0000; // Media Board Type (Type-1 vs Type-3), 0x0000 = Type-1, 0x0100 = Type-3
case 0x40F4: return 0x03; // 1GB
}
printf("MediaBoard::LpcRead: Unknown Addr %08X\n", addr);
return 0;
}
void MediaBoard::LpcWrite(uint32_t addr, uint32_t value, int size)
{
switch (addr) {
case 0x40E1: HalSystemInterrupts[10].Assert(false); break;
default:
printf("MediaBoard::LpcWrite: Unknown Addr %08X = %08X\n", addr, value);
break;
}
}
void MediaBoard::ComRead(uint32_t offset, void* buffer, uint32_t length)
{
// Copy the current read buffer to the output
memcpy(buffer, readBuffer, 0x20);
}
void MediaBoard::ComWrite(uint32_t offset, void* buffer, uint32_t length)
{
// Instant replies cause race conditions, software seems to expect at least a little delay
Sleep(100);
if (offset == 0x900000) { // Some kind of reset?
memcpy(readBuffer, buffer, 0x20);
return;
} else if (offset == 0x900200) { // Command Sector
// Copy the written data to our internal, so we don't trash the original data
memcpy(writeBuffer, buffer, 0x20);
// Create accessor pointers
auto inputBuffer16 = (uint16_t*)writeBuffer;
auto inputBuffer32 = (uint32_t*)writeBuffer;
auto outputBuffer16 = (uint16_t*)readBuffer;
auto outputBuffer32 = (uint32_t*)readBuffer;
// If no command word was specified, do nothing
if (inputBuffer16[0] == 0) {
return;
}
// First word of output gets set to first word of the input, second word gets OR'D with ACK
outputBuffer16[0] = inputBuffer16[0];
outputBuffer16[1] = inputBuffer16[1] | 0x8000; // ACK?
// Read the given Command and handle it
uint32_t command = inputBuffer16[1];
switch (command) {
case MB_CMD_DIMM_SIZE:
outputBuffer32[1] = 1024 * ONE_MB;
break;
case MB_CMD_STATUS:
outputBuffer32[1] = MB_STATUS_READY;
outputBuffer32[2] = 100; // Load/Test Percentage (0-100)
break;
case MB_CMD_FIRMWARE_VERSION:
outputBuffer32[1] = 0x0317;
break;
case MB_CMD_SYSTEM_TYPE:
outputBuffer32[1] = MB_SYSTEM_TYPE_DEVELOPER | MB_SYSTEM_TYPE_GDROM;
break;
case MB_CMD_SERIAL_NUMBER:
memcpy(&outputBuffer32[1], "A89E-25A47354512", 17);
break;
case MB_CMD_HARDWARE_TEST: {
uint32_t testType = inputBuffer32[1];
xbox::addr_xt resultWritePtr = inputBuffer32[2];
outputBuffer32[1] = inputBuffer32[1];
printf("Perform Test Type %X, place result at %08X\n", testType, resultWritePtr);
// For now, just pretend we did the test and was successful
// TODO: How to report percentage? Get's stuck on "CHECKING 0% but still shows "TEST OK"
memcpy((void*)resultWritePtr, "TEST OK", 8);
} break;
default: printf("Unhandled MediaBoard Command: %04X\n", command);
}
// Clear the command bytes
inputBuffer16[0] = 0;
inputBuffer16[1] = 0;
// Trigger LPC Interrupt
HalSystemInterrupts[10].Assert(true);
return;
}
printf("Unhandled MediaBoard mbcom: offset %08X\n", offset);
}

View file

@ -1,98 +0,0 @@
// ******************************************************************
// * src->devices->chihiro->MediaBoard.h
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them 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 recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2019 Luke Usher
// *
// * All rights reserved
// *
// ******************************************************************
#ifndef MEDIABOARD_H
#define MEDIABOARD_H
#include <cstdint>
#include <string>
#define MB_CMD_DIMM_SIZE 0x0001
#define MB_CMD_STATUS 0x0100
#define MB_STATUS_INIT 0
#define MB_STATUS_CHECKING_NETWORK 1
#define MB_STATUS_SYSTEM_DISC 2
#define MB_STATUS_TESTING 3
#define MB_STATUS_LOADING 4
#define MB_STATUS_READY 5
#define MB_STATUS_ERROR 6
#define MB_CMD_FIRMWARE_VERSION 0x0101
#define MB_CMD_SYSTEM_TYPE 0x0102
#define MB_SYSTEM_TYPE_DEVELOPER 0x8000
#define MB_SYSTEM_TYPE_GDROM 0x0001
#define MB_CMD_SERIAL_NUMBER 0x0103
#define MB_CMD_HARDWARE_TEST 0x0301
#define MB_CHIHIRO_REGION_FLAG_JAPAN 0x2
#define MB_CHIHIRO_REGION_FLAG_USA 0x4
#define MB_CHIHIRO_REGION_FLAG_EXPORT 0x8
typedef struct {
char magic[4]; // 0x00 (Always BTID)
uint32_t unknown0[3];
uint32_t unknown1[4];
char mediaboardType[4]; // 0x20 (XBAM for Chihiro)
uint32_t unknown2;
uint16_t year; // 0x28
uint8_t month; // 0x2A
uint8_t day; // 0x2B
uint8_t videoMode; // 0x2C unknown bitmask, resolutions + horizontal/vertical
uint8_t unknown3;
uint8_t type3Compatible; // 0x2E (Type-3 compatible titles have this set to 1)
uint8_t unknown4;
char gameId[8]; // 0x30
uint32_t regionFlags; // 0x38
uint32_t unknown6[9];
char manufacturer[0x20]; // 0x60
char gameName[0x20]; // 0x80
char gameExecutable[0x20]; // 0xA0
char testExecutable[0x20]; // 0xC0
char creditTypes[8][0x20]; // 0xE0
} chihiro_bootid;
class MediaBoard
{
public:
void SetMountPath(std::string path);
// LPC IO handlers
uint32_t LpcRead(uint32_t addr, int size);
void LpcWrite(uint32_t addr, uint32_t value, int size);
// Mbcom partition handlers
void ComRead(uint32_t offset, void* buffer, uint32_t length);
void ComWrite(uint32_t offset, void* buffer, uint32_t length);
chihiro_bootid &GetBootId();
private:
uint8_t readBuffer[512];
uint8_t writeBuffer[512];
std::string m_MountPath;
chihiro_bootid BootID;
};
#endif

View file

@ -476,18 +476,15 @@ void EmuNVNet_Write(xbox::addr_xt addr, uint32_t value, int size)
}
std::thread NVNetRecvThread;
void NVNetRecvThreadProc()
static void NVNetRecvThreadProc(NvNetState_t *s)
{
// NOTE: profiling shows that the winpcap function can take up to 1/6th of the total cpu time of the loader process, so avoid placing
// this function in system_events
g_AffinityPolicy->SetAffinityOther();
static std::unique_ptr<uint8_t[]> packet(new uint8_t[65536]);
uint8_t packet[65536];
while (true) {
int size = g_NVNet->PCAPReceive(packet.get(), 65536);
int size = g_NVNet->PCAPReceive(packet, 65536);
if (size > 0) {
EmuNVNet_DMAPacketToGuest(packet.get(), size);
}
_mm_pause();
EmuNVNet_DMAPacketToGuest(packet, size);
}
}
}
@ -530,7 +527,7 @@ void NVNetDevice::Init()
};
PCAPInit();
NVNetRecvThread = std::thread(NVNetRecvThreadProc);
NVNetRecvThread = std::thread(NVNetRecvThreadProc, &NvNetState);
}
void NVNetDevice::Reset()

View file

@ -279,6 +279,11 @@ OHCI::OHCI(USBDevice* UsbObj)
OHCI_StateReset();
}
void OHCI::OHCI_FrameBoundaryWrapper(void* pVoid)
{
static_cast<OHCI*>(pVoid)->OHCI_FrameBoundaryWorker();
}
void OHCI::OHCI_FrameBoundaryWorker()
{
OHCI_HCCA hcca;
@ -353,7 +358,7 @@ void OHCI::OHCI_FrameBoundaryWorker()
}
// Do SOF stuff here
OHCI_SOF();
OHCI_SOF(false);
// Writeback HCCA
if (OHCI_WriteHCCA(m_Registers.HcHCCA, &hcca)) {
@ -872,23 +877,32 @@ void OHCI::OHCI_StateReset()
void OHCI::OHCI_BusStart()
{
// Create the EOF timer.
m_pEOFtimer = true;
m_pEOFtimer = Timer_Create(OHCI_FrameBoundaryWrapper, this, "", false);
EmuLog(LOG_LEVEL::DEBUG, "Operational event");
// SOF event
OHCI_SOF();
OHCI_SOF(true);
}
void OHCI::OHCI_BusStop()
{
m_pEOFtimer = false;
if (m_pEOFtimer) {
// Delete existing EOF timer
Timer_Exit(m_pEOFtimer);
}
m_pEOFtimer = nullptr;
}
void OHCI::OHCI_SOF()
void OHCI::OHCI_SOF(bool bCreate)
{
// set current SOF time
m_SOFtime = get_now();
m_SOFtime = GetTime_NS(m_pEOFtimer);
// make timer expire at SOF + 1 ms from now
if (bCreate) {
Timer_Start(m_pEOFtimer, m_UsbFrameTime);
}
OHCI_SetInterrupt(OHCI_INTR_SF);
}
@ -1240,23 +1254,6 @@ void OHCI::OHCI_WriteRegister(xbox::addr_xt Addr, uint32_t Value)
}
}
uint64_t OHCI::OHCI_next(uint64_t now)
{
if (m_pEOFtimer) {
constexpr uint64_t ohci_period = 1000;
uint64_t next = m_SOFtime + ohci_period;
if (now >= next) {
OHCI_FrameBoundaryWorker();
return ohci_period;
}
return m_SOFtime + ohci_period - now; // time remaining until EOF
}
return -1;
}
void OHCI::OHCI_UpdateInterrupt()
{
if ((m_Registers.HcInterrupt & OHCI_INTR_MIE) && (m_Registers.HcInterruptStatus & m_Registers.HcInterrupt)) {
@ -1281,7 +1278,7 @@ uint32_t OHCI::OHCI_GetFrameRemaining()
}
// Being in USB operational state guarantees that m_pEOFtimer and m_SOFtime were set already
ticks = get_now() - m_SOFtime;
ticks = GetTime_NS(m_pEOFtimer) - m_SOFtime;
// Avoid Muldiv64 if possible
if (ticks >= m_UsbFrameTime) {

View file

@ -145,10 +145,6 @@ class OHCI
uint32_t OHCI_ReadRegister(xbox::addr_xt Addr);
// write a register
void OHCI_WriteRegister(xbox::addr_xt Addr, uint32_t Value);
// calculates when the next EOF is due
uint64_t OHCI_next(uint64_t now);
// EOF callback function
void OHCI_FrameBoundaryWorker();
private:
@ -157,7 +153,7 @@ class OHCI
// all the registers available in the OHCI standard
OHCI_Registers m_Registers;
// end-of-frame timer
bool m_pEOFtimer = false;
TimerObject* m_pEOFtimer = nullptr;
// time at which a SOF was sent
uint64_t m_SOFtime;
// the duration of a usb frame
@ -177,6 +173,10 @@ class OHCI
// indicates if there is a pending asynchronous packet to process
int m_AsyncComplete = 0;
// EOF callback wrapper
static void OHCI_FrameBoundaryWrapper(void* pVoid);
// EOF callback function
void OHCI_FrameBoundaryWorker();
// inform the HCD that we got a problem here...
void OHCI_FatalError();
// initialize packet struct
@ -189,8 +189,8 @@ class OHCI
void OHCI_BusStart();
// stop sending SOF tokens across the usb bus
void OHCI_BusStop();
// generate a SOF event
void OHCI_SOF();
// generate a SOF event, and start a timer for EOF
void OHCI_SOF(bool bCreate);
// change interrupt status
void OHCI_UpdateInterrupt();
// fire an interrupt

View file

@ -82,7 +82,6 @@ DEVICE_WRITE32(PRAMDAC)
} else {
d->pramdac.core_clock_freq = (NV2A_CRYSTAL_FREQ * n)
/ (1 << p) / m;
d->ptimer_period = ((uint64_t(d->ptimer.alarm_time >> 5) * SCALE_S_IN_US) / d->pramdac.core_clock_freq);
}
break;

View file

@ -35,13 +35,17 @@
#include "common\util\CxbxUtil.h"
#define NANOSECONDS_PER_SECOND 1000000000
/* PTIMER - time measurement and time-based alarms */
static uint64_t ptimer_get_clock(NV2AState *d)
static uint64_t ptimer_get_clock(NV2AState * d)
{
return Muldiv64(Muldiv64(get_now(),
// Get time in nanoseconds
uint64_t time = std::chrono::duration<uint64_t, std::nano>(std::chrono::steady_clock::now().time_since_epoch()).count();
return Muldiv64(Muldiv64(time,
(uint32_t)d->pramdac.core_clock_freq, // TODO : Research how this can be updated to accept uint64_t
SCALE_S_IN_US), // Was CLOCKS_PER_SEC
NANOSECONDS_PER_SECOND), // Was CLOCKS_PER_SEC
d->ptimer.denominator,
d->ptimer.numerator);
}
@ -87,13 +91,6 @@ DEVICE_WRITE32(PTIMER)
break;
case NV_PTIMER_INTR_EN_0:
d->ptimer.enabled_interrupts = value;
if (d->ptimer.enabled_interrupts & NV_PTIMER_INTR_EN_0_ALARM) {
d->ptimer_last = get_now();
d->ptimer_active = true;
}
else if ((d->ptimer.enabled_interrupts & NV_PTIMER_INTR_EN_0_ALARM) == 0) {
d->ptimer_active = false;
}
update_irq(d);
break;
case NV_PTIMER_DENOMINATOR:
@ -104,7 +101,6 @@ DEVICE_WRITE32(PTIMER)
break;
case NV_PTIMER_ALARM_0:
d->ptimer.alarm_time = value;
d->ptimer_period = ((uint64_t(d->ptimer.alarm_time >> 5) * SCALE_S_IN_US) / d->pramdac.core_clock_freq);
break;
default:
//DEVICE_WRITE32_REG(ptimer); // Was : DEBUG_WRITE32_UNHANDLED(PTIMER);

View file

@ -51,7 +51,6 @@
#include "core\kernel\init\CxbxKrnl.h" // For XBOX_MEMORY_SIZE, DWORD, etc
#include "core\kernel\support\Emu.h"
#include "core\kernel\support\NativeHandle.h"
#include "core\kernel\exports\EmuKrnl.h"
#include <backends/imgui_impl_win32.h>
#include <backends/imgui_impl_opengl3.h>
@ -59,7 +58,6 @@
#include "core\hle\Intercept.hpp"
#include "common/win32/Threads.h"
#include "Logging.h"
#include "Timer.h"
#include "vga.h"
#include "nv2a.h" // For NV2AState
@ -130,14 +128,6 @@ static void update_irq(NV2AState *d)
d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PVIDEO;
}
/* PTIMER */
if (d->ptimer.pending_interrupts & d->ptimer.enabled_interrupts) {
d->pmc.pending_interrupts |= NV_PMC_INTR_0_PTIMER;
}
else {
d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PTIMER;
}
/* TODO : PBUS * /
if (d->pbus.pending_interrupts & d->pbus.enabled_interrupts) {
d->pmc.pending_interrupts |= NV_PMC_INTR_0_PBUS;
@ -329,8 +319,8 @@ const NV2ABlockInfo* EmuNV2A_Block(xbox::addr_xt addr)
// HACK: Until we implement VGA/proper interrupt generation
// we simulate VBLANK by calling the interrupt at 60Hz
std::thread vblank_thread;
extern std::chrono::steady_clock::time_point GetNextVBlankTime();
extern void hle_vblank();
void _check_gl_reset()
{
@ -1107,27 +1097,25 @@ void NV2ADevice::UpdateHostDisplay(NV2AState *d)
}
// TODO: Fix this properly
template<bool should_update_hle>
void nv2a_vblank_interrupt(void *opaque)
static void nv2a_vblank_thread(NV2AState *d)
{
NV2AState *d = static_cast<NV2AState *>(opaque);
g_AffinityPolicy->SetAffinityOther();
CxbxSetThreadName("Cxbx NV2A VBLANK");
auto nextVBlankTime = GetNextVBlankTime();
if (!d->exiting) [[likely]] {
d->pcrtc.pending_interrupts |= NV_PCRTC_INTR_0_VBLANK;
update_irq(d);
while (!d->exiting) {
// Handle VBlank
if (std::chrono::steady_clock::now() > nextVBlankTime) {
d->pcrtc.pending_interrupts |= NV_PCRTC_INTR_0_VBLANK;
update_irq(d);
nextVBlankTime = GetNextVBlankTime();
// trigger the gpu interrupt if it was asserted in update_irq
if (g_bEnableAllInterrupts && HalSystemInterrupts[3].IsPending() && EmuInterruptList[3] && EmuInterruptList[3]->Connected) {
HalSystemInterrupts[3].Trigger(EmuInterruptList[3]);
// TODO: We should swap here for the purposes of supporting overlays + direct framebuffer access
// But it causes crashes on AMD hardware for reasons currently unknown...
//NV2ADevice::UpdateHostDisplay(d);
}
// TODO: We should swap here for the purposes of supporting overlays + direct framebuffer access
// But it causes crashes on AMD hardware for reasons currently unknown...
//NV2ADevice::UpdateHostDisplay(d);
if constexpr (should_update_hle) {
hle_vblank();
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
@ -1201,8 +1189,8 @@ void NV2ADevice::Init()
d->vram_ptr = (uint8_t*)PHYSICAL_MAP_BASE;
d->vram_size = g_SystemMaxMemory;
d->pramdac.core_clock_coeff = 0x00011C01; /* 233MHz...? */
d->pramdac.core_clock_freq = 233333324;
d->pramdac.core_clock_coeff = 0x00011c01; /* 189MHz...? */
d->pramdac.core_clock_freq = 189000000;
d->pramdac.memory_clock_coeff = 0;
d->pramdac.video_clock_coeff = 0x0003C20D; /* 25182Khz...? */
@ -1214,13 +1202,7 @@ void NV2ADevice::Init()
pvideo_init(d);
}
d->vblank_last = get_now();
if (bLLE_GPU) {
d->vblank_cb = nv2a_vblank_interrupt<false>;
}
else {
d->vblank_cb = nv2a_vblank_interrupt<true>;
}
vblank_thread = std::thread(nv2a_vblank_thread, d);
qemu_mutex_init(&d->pfifo.pfifo_lock);
qemu_cond_init(&d->pfifo.puller_cond);
@ -1245,8 +1227,9 @@ void NV2ADevice::Reset()
qemu_cond_broadcast(&d->pfifo.pusher_cond);
d->pfifo.puller_thread.join();
d->pfifo.pusher_thread.join();
qemu_mutex_destroy(&d->pfifo.pfifo_lock); // Cxbxr addition
qemu_mutex_destroy(&d->pfifo.pfifo_lock); // Cbxbx addition
if (d->pgraph.opengl_enabled) {
vblank_thread.join();
pvideo_destroy(d);
}
@ -1394,45 +1377,3 @@ int NV2ADevice::GetFrameWidth(NV2AState* d)
return width;
}
uint64_t NV2ADevice::vblank_next(uint64_t now)
{
// TODO: this should use a vblank period of 20ms when we are in 50Hz PAL mode
constexpr uint64_t vblank_period = 16.6666666667 * 1000;
uint64_t next = m_nv2a_state->vblank_last + vblank_period;
if (now >= next) {
m_nv2a_state->vblank_cb(m_nv2a_state);
m_nv2a_state->vblank_last = get_now();
return vblank_period;
}
return m_nv2a_state->vblank_last + vblank_period - now; // time remaining until next vblank
}
uint64_t NV2ADevice::ptimer_next(uint64_t now)
{
// Test case: Dead or Alive Ultimate uses this when in PAL50 mode only
if (m_nv2a_state->ptimer_active) {
const uint64_t ptimer_period = m_nv2a_state->ptimer_period;
uint64_t next = m_nv2a_state->ptimer_last + ptimer_period;
if (now >= next) {
if (!m_nv2a_state->exiting) [[likely]] {
m_nv2a_state->ptimer.pending_interrupts |= NV_PTIMER_INTR_0_ALARM;
update_irq(m_nv2a_state);
// trigger the gpu interrupt if it was asserted in update_irq
if (g_bEnableAllInterrupts && HalSystemInterrupts[3].IsPending() && EmuInterruptList[3] && EmuInterruptList[3]->Connected) {
HalSystemInterrupts[3].Trigger(EmuInterruptList[3]);
}
}
m_nv2a_state->ptimer_last = get_now();
return ptimer_period;
}
return m_nv2a_state->ptimer_last + ptimer_period - now; // time remaining until next ptimer interrupt
}
return -1;
}

View file

@ -108,10 +108,6 @@ public:
static int GetFrameWidth(NV2AState *d);
static int GetFrameHeight(NV2AState *d);
uint64_t vblank_next(uint64_t now);
uint64_t ptimer_next(uint64_t now);
private:
NV2AState *m_nv2a_state;
};

Some files were not shown because too many files have changed in this diff Show more