mirror of
https://github.com/Cxbx-Reloaded/Cxbx-Reloaded.git
synced 2025-04-02 11:11:52 -04:00
Compare commits
150 commits
CI-4204640
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
dd36dd598c | ||
|
87634a2e27 | ||
|
6f32d89545 | ||
|
ec0c288bc4 | ||
|
8bfbcb56fd | ||
|
8965d2443b | ||
|
b33ed95c5b | ||
|
8ee17b512c | ||
|
50334cbc31 | ||
|
41454b8c26 | ||
|
204dcf8801 | ||
|
77c63ceec3 | ||
|
2cfaba893e | ||
|
17b0cb81d4 | ||
|
daa6a816ff | ||
|
6caf3ea679 | ||
|
9a58823b70 | ||
|
3edd8d168b | ||
|
f894d31332 | ||
|
9241bec768 | ||
|
c50a0c5c7d | ||
|
87bab04932 | ||
|
ad6769bbf3 | ||
|
0e63131fc3 | ||
|
889040c56a | ||
|
86542c9f2e | ||
|
c9edbd1003 | ||
|
ebb122f2a0 | ||
|
c158a472ff | ||
|
6961d1c7a1 | ||
|
2f7cfe7e95 | ||
|
46d0173673 | ||
|
c7b028b3e7 | ||
|
3d12edc77d | ||
|
08ab4b9164 | ||
|
4fca5c7007 | ||
|
e26f20108a | ||
|
8475124e5b | ||
|
9b2ae106e5 | ||
|
b3bfeca3a8 | ||
|
b77a13b708 | ||
|
1b5e111ae3 | ||
|
1504a75a46 | ||
|
87496ab873 | ||
|
5b37a7ec21 | ||
|
639f42c318 | ||
|
8d92992a6b | ||
|
7323eed73e | ||
|
b47c1f195c | ||
|
7c73bfc525 | ||
|
1b4a3bb54f | ||
|
750d202fa8 | ||
|
e7bca5e1bf | ||
|
937ab9e1c2 | ||
|
8006f55cf3 | ||
|
1828ddfd6f | ||
|
bc42cfaa6b | ||
|
b1235b7733 | ||
|
1615ecc976 | ||
|
0007d20b03 | ||
|
bfb10092c0 | ||
|
f5b4878245 | ||
|
4174fbc23f | ||
|
0560ed6955 | ||
|
875015164c | ||
|
aedb5ba87b | ||
|
e5dcdebe7f | ||
|
ad0b8340da | ||
|
7298b6c4dc | ||
|
b20db36e15 | ||
|
a03d50df56 | ||
|
0e25897f77 | ||
|
c6049b768c | ||
|
399baccadb | ||
|
3edc388abf | ||
|
796e8d2beb | ||
|
282c5f5622 | ||
|
8e5b27d054 | ||
|
b64a3b6faa | ||
|
d64e172c9f | ||
|
d6b96b8ea1 | ||
|
131b330a85 | ||
|
bf6193202a | ||
|
06c28a847e | ||
|
8cc9c73f58 | ||
|
2452965580 | ||
|
eddc14e151 | ||
|
31ff15ba1d | ||
|
4d9151ca26 | ||
|
1f1d1ac631 | ||
|
e5043dbc05 | ||
|
a2fb41856d | ||
|
b09d3ca69a | ||
|
684d3338f2 | ||
|
ae140bb6bf | ||
|
a5b8f15a14 | ||
|
2c8a764fc7 | ||
|
605271245c | ||
|
93e36f7be3 | ||
|
a7bc6a307d | ||
|
7ad047bcea | ||
|
0f21e25d7d | ||
|
79884bdf3d | ||
|
260e2fb7c8 | ||
|
4d221c3c81 | ||
|
712d3bee2f | ||
|
3cd551d827 | ||
|
397f33143d | ||
|
c7e75d7c5c | ||
|
4808be65c4 | ||
|
def10ff466 | ||
|
e1ea10c4cb | ||
|
67f21d5c30 | ||
|
971318a89a | ||
|
b62d39da7d | ||
|
6c530fbf86 | ||
|
a8f6d0496e | ||
|
6389cb6524 | ||
|
ef3439e46f | ||
|
ed8a6124e4 | ||
|
b1bd9dd5d0 | ||
|
062752e1a7 | ||
|
99ab34ac82 | ||
|
8e0df988a8 | ||
|
58041c95b4 | ||
|
827a3212f8 | ||
|
d0890d588d | ||
|
9e9d3f390f | ||
|
4821a72b6f | ||
|
bf1483ae56 | ||
|
b2f05b8b0b | ||
|
111728f170 | ||
|
ce05ea1397 | ||
|
f4488c0270 | ||
|
65a5ad6591 | ||
|
0b695637ce | ||
|
fe9a706a8e | ||
|
8ac5d14cd2 | ||
|
628323218a | ||
|
f7c09ddc4f | ||
|
09e744ecc4 | ||
|
a37124e2dc | ||
|
5f58ae918c | ||
|
f17b7f7fa6 | ||
|
4dccf6d5b9 | ||
|
6bbe6cefe8 | ||
|
547c3ae663 | ||
|
c594e34ac5 | ||
|
caae99952c | ||
|
aeeb67dc6a |
105 changed files with 4544 additions and 2206 deletions
112
.github/labeler.yml
vendored
112
.github/labeler.yml
vendored
|
@ -1,75 +1,111 @@
|
|||
# Labels are in alphabetical order.
|
||||
|
||||
cmake:
|
||||
- 'CMake*'
|
||||
- '**/CMakeLists.txt'
|
||||
- '**/*.cmake'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'CMake*'
|
||||
- '**/CMakeLists.txt'
|
||||
- '**/*.cmake'
|
||||
|
||||
cpu-emulation:
|
||||
- 'src/devices/x86/**'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/devices/x86/**'
|
||||
|
||||
deployment:
|
||||
- '*.yml'
|
||||
- '.github/workflows/CI.yml'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- '*.yml'
|
||||
- '.github/workflows/CI.yml'
|
||||
|
||||
file-system:
|
||||
- 'src/core/kernel/support/EmuFile*'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/kernel/support/EmuFile*'
|
||||
|
||||
graphics:
|
||||
- 'src/core/hle/D3D8/**'
|
||||
- 'src/core/hle/XGRAPHIC/**'
|
||||
- 'src/devices/video/**'
|
||||
- 'src/gui/*Video*'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/hle/D3D8/**'
|
||||
- 'src/core/hle/XGRAPHIC/**'
|
||||
- 'src/devices/video/**'
|
||||
- 'src/gui/*Video*'
|
||||
|
||||
HLE:
|
||||
- 'src/core/hle/**'
|
||||
- 'src/core/kernel/**'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/hle/**'
|
||||
- 'src/core/kernel/**'
|
||||
|
||||
informational:
|
||||
- '**/*Logging*'
|
||||
- '**/*Logging*/**'
|
||||
- '**/README.md'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- '**/*Logging*'
|
||||
- '**/*Logging*/**'
|
||||
- '**/README.md'
|
||||
|
||||
input:
|
||||
- 'src/common/input/**'
|
||||
- 'src/core/hle/XAPI/input/**'
|
||||
- 'src/devices/usb/**'
|
||||
- 'src/gui/controllers/**'
|
||||
- 'src/gui/*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*'
|
||||
|
||||
kernel:
|
||||
- 'src/core/kernel/**'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/kernel/**'
|
||||
|
||||
LLE:
|
||||
- 'src/devices/**'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/devices/**'
|
||||
|
||||
memory:
|
||||
- 'src/core/kernel/memory-manager/**'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/kernel/memory-manager/**'
|
||||
|
||||
networking:
|
||||
- 'src/core/hle/XONLINE/**'
|
||||
- 'src/devices/network/**'
|
||||
- 'src/gui/*Network*'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/hle/XONLINE/**'
|
||||
- 'src/devices/network/**'
|
||||
- 'src/gui/*Network*'
|
||||
|
||||
sound:
|
||||
- 'src/common/audio/**'
|
||||
- 'src/core/hle/DSOUND/**'
|
||||
- 'src/core/hle/XACTENG/**'
|
||||
- 'src/devices/audio/**'
|
||||
- 'src/gui/*Audio*'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/common/audio/**'
|
||||
- 'src/core/hle/DSOUND/**'
|
||||
- 'src/core/hle/XACTENG/**'
|
||||
- 'src/devices/audio/**'
|
||||
- 'src/gui/*Audio*'
|
||||
|
||||
modules:
|
||||
- 'import/**'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'import/**'
|
||||
|
||||
threading:
|
||||
- 'src/core/kernel/support/EmuFS*'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/kernel/support/EmuFS*'
|
||||
|
||||
timing:
|
||||
- 'src/common/Timer*'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/common/Timer*'
|
||||
|
||||
user interface:
|
||||
- 'src/core/common/imgui/*'
|
||||
- 'src/gui/**'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/core/common/imgui/*'
|
||||
- 'src/gui/**'
|
||||
|
||||
xbdm:
|
||||
- 'src/common/xbdm/**'
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'src/common/xbdm/**'
|
||||
|
|
16
.github/workflows/CI.yml
vendored
16
.github/workflows/CI.yml
vendored
|
@ -30,19 +30,21 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
configuration: [Release, Debug]
|
||||
vsver: [2022]
|
||||
vsver: [2019]
|
||||
winver: [7]
|
||||
sdkver: [10.0.22621.0]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Generate CMake files
|
||||
run: cmake -B build -A Win32
|
||||
run: cmake -B build -A Win32,version=${{ matrix.sdkver }} -DCMAKE_SYSTEM_VERSION=${{ matrix.winver }}
|
||||
- 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@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: matrix.configuration == 'Release'
|
||||
with:
|
||||
name: CxbxReloaded-${{ matrix.configuration }}-VS${{ matrix.vsver }}
|
||||
|
@ -57,9 +59,9 @@ jobs:
|
|||
needs: build-windows
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
- name: Re-zip artifacts
|
||||
|
@ -72,7 +74,7 @@ jobs:
|
|||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "::set-output name=tag_name::CI-${GITHUB_SHA::7}"
|
||||
echo "tag_name=CI-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
|
||||
- name: Create Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
4
.github/workflows/autoclose.yml
vendored
4
.github/workflows/autoclose.yml
vendored
|
@ -7,9 +7,9 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Automatically close issues that don't follow the issue template
|
||||
uses: ergo720/auto-close-issues@v1.0.4
|
||||
uses: ergo720/auto-close-issues@v1
|
||||
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
|
||||
|
|
2
.github/workflows/pull-request.yml
vendored
2
.github/workflows/pull-request.yml
vendored
|
@ -7,7 +7,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Labeler
|
||||
uses: actions/labeler@v4
|
||||
uses: actions/labeler@v5
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sync-labels: true
|
||||
|
|
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -1,6 +1,6 @@
|
|||
[submodule "import/subhook"]
|
||||
path = import/subhook
|
||||
url = https://github.com/Zeex/subhook.git
|
||||
url = https://github.com/Cxbx-Reloaded/subhook.git
|
||||
shallow = true
|
||||
[submodule "import/cs_x86"]
|
||||
path = import/cs_x86
|
||||
|
@ -43,3 +43,7 @@
|
|||
[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
|
||||
|
|
|
@ -22,6 +22,8 @@ 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})
|
||||
|
||||
|
@ -77,7 +79,6 @@ 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"
|
||||
|
@ -144,6 +145,7 @@ 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"
|
||||
|
@ -152,7 +154,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/VertexShaderSource.h"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderCache.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"
|
||||
|
@ -252,9 +254,7 @@ 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/VertexShaderSource.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderCache.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,6 +348,7 @@ 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"
|
||||
|
@ -378,6 +379,8 @@ 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"
|
||||
|
@ -471,14 +474,19 @@ install(FILES ${cxbxr_INSTALL_files}
|
|||
DESTINATION bin
|
||||
)
|
||||
|
||||
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
|
||||
# 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(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")
|
||||
|
||||
|
|
|
@ -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
|
||||
* [WinUSB compliant driver](https://github.com/libusb/libusb/wiki/Windows#Driver_Installation)
|
||||
* *Optional, only needed for USB pass-through of original Xbox and Steel Battalion controllers.*
|
||||
|
||||
### Wine
|
||||
|
|
2
import/SDL2
vendored
2
import/SDL2
vendored
|
@ -1 +1 @@
|
|||
Subproject commit b424665e0899769b200231ba943353a5fee1b6b6
|
||||
Subproject commit fa24d868ac2f8fd558e4e914c9863411245db8fd
|
2
import/XbSymbolDatabase
vendored
2
import/XbSymbolDatabase
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 75ce58fa8d135ef0a75bee729cde9542eda393b6
|
||||
Subproject commit 774111351210e6f340246d6fb32741b09708f381
|
1
import/mio
vendored
Submodule
1
import/mio
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 3f86a95c0784d73ce6815237ec33ed25f233b643
|
|
@ -42,8 +42,11 @@ 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
|
||||
|
@ -65,7 +68,7 @@ XXH_INLINE_ALL
|
|||
)
|
||||
|
||||
file (GLOB RESOURCES
|
||||
|
||||
|
||||
"${CXBXR_ROOT_DIR}/CONTRIBUTORS"
|
||||
"${CXBXR_ROOT_DIR}/COPYING"
|
||||
"${CXBXR_ROOT_DIR}/README.md"
|
||||
|
@ -87,7 +90,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}
|
||||
)
|
||||
|
@ -118,7 +121,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
|
||||
# /Ob2 = Controls inline expansion of functions.
|
||||
# /Ob3 = Controls inline expansion of functions.
|
||||
# /Oi = Generate intrinsic functions
|
||||
# /Ot = In favor of using fast code than small code
|
||||
# /GL = Whole program optimization
|
||||
|
@ -129,7 +132,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
# Set optimization options for release build
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} \
|
||||
/Zi \
|
||||
/Ob2 \
|
||||
/Ob3 \
|
||||
/Oi \
|
||||
/Ot \
|
||||
/GL \
|
||||
|
@ -139,7 +142,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-"
|
||||
|
@ -147,7 +150,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
endif()
|
||||
|
||||
# Windows libraries
|
||||
set(WINS_LIB
|
||||
set(WINS_LIB
|
||||
legacy_stdio_definitions
|
||||
d3d9
|
||||
d3dcompiler
|
||||
|
@ -167,6 +170,7 @@ set(WINS_LIB
|
|||
comctl32
|
||||
XINPUT9_1_0
|
||||
Iphlpapi
|
||||
Dwmapi
|
||||
)
|
||||
|
||||
target_link_libraries(cxbx
|
||||
|
@ -176,6 +180,7 @@ target_link_libraries(cxbx
|
|||
SDL2
|
||||
imgui
|
||||
libusb
|
||||
mio::mio_min_winapi
|
||||
|
||||
${WINS_LIB}
|
||||
)
|
||||
|
|
|
@ -48,6 +48,9 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
|
||||
# Use inline XXHash version
|
||||
XXH_INLINE_ALL
|
||||
|
||||
# Enable Chihiro work
|
||||
CHIHIRO_WORK
|
||||
)
|
||||
add_compile_options(
|
||||
/EHs
|
||||
|
@ -128,7 +131,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
# Set optimization options for release build
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} \
|
||||
/Zi \
|
||||
/Ob2 \
|
||||
/Ob3 \
|
||||
/Oi \
|
||||
/Ot \
|
||||
/GL \
|
||||
|
@ -141,7 +144,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
endif()
|
||||
|
||||
# Windows libraries
|
||||
set(WINS_LIB
|
||||
set(WINS_LIB
|
||||
legacy_stdio_definitions
|
||||
d3d9
|
||||
d3dcompiler
|
||||
|
@ -172,6 +175,7 @@ target_link_libraries(cxbxr-emu
|
|||
imgui
|
||||
libusb
|
||||
nv2a_vsh_emulator
|
||||
mio::mio_min_winapi
|
||||
|
||||
${WINS_LIB}
|
||||
)
|
||||
|
|
|
@ -27,14 +27,3 @@ 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})
|
||||
|
|
|
@ -84,6 +84,4 @@ extern volatile bool g_bPrintfOn;
|
|||
#define CxbxSetThreadName(Name)
|
||||
#endif
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#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"
|
||||
|
|
|
@ -31,6 +31,8 @@ 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.
|
||||
|
|
|
@ -49,8 +49,9 @@ 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_LOGGING_SYNC = 0,
|
||||
CONFIG_INPUT_SYNC,
|
||||
CONFIG_CHANGE_TIME
|
||||
} IPC_UPDATE_KERNEL;
|
||||
|
||||
void ipc_send_kernel_update(IPC_UPDATE_KERNEL command, const int value, const unsigned int hwnd);
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
// ******************************************************************
|
||||
#ifndef SETTINGS_HPP
|
||||
#define SETTINGS_HPP
|
||||
#include "Cxbx.h"
|
||||
|
||||
#include "SimpleIni.h"
|
||||
#include "common\input\InputManager.h"
|
||||
|
|
|
@ -25,19 +25,45 @@
|
|||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <core\kernel\exports\xboxkrnl.h>
|
||||
|
||||
#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"
|
||||
#ifdef __linux__
|
||||
#include <time.h>
|
||||
#endif
|
||||
#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;
|
||||
}
|
||||
|
||||
// More precise sleep, but with increased CPU usage
|
||||
void SleepPrecise(std::chrono::steady_clock::time_point targetTime)
|
||||
|
@ -69,174 +95,83 @@ void SleepPrecise(std::chrono::steady_clock::time_point targetTime)
|
|||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
// NOTE: the pit device is not implemented right now, so we put this here
|
||||
static uint64_t pit_next(uint64_t now)
|
||||
{
|
||||
#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;
|
||||
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
|
||||
}
|
||||
|
||||
// Calculates the next expire time of the timer
|
||||
static inline uint64_t GetNextExpireTime(TimerObject* Timer)
|
||||
static void update_non_periodic_events()
|
||||
{
|
||||
return GetTime_NS(Timer) + Timer->ExpireTime_MS.load();
|
||||
// update dsound
|
||||
dsound_worker();
|
||||
|
||||
// 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)
|
||||
uint64_t get_now()
|
||||
{
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
void Timer_Shutdown()
|
||||
static uint64_t get_next(uint64_t now)
|
||||
{
|
||||
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();
|
||||
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());
|
||||
}
|
||||
|
||||
// Thread that runs the timer
|
||||
void NTAPI ClockThread(void *TimerArg)
|
||||
xbox::void_xt NTAPI system_events(xbox::PVOID arg)
|
||||
{
|
||||
TimerObject *Timer = static_cast<TimerObject *>(TimerArg);
|
||||
if (!Timer->Name.empty()) {
|
||||
CxbxSetThreadName(Timer->Name.c_str());
|
||||
}
|
||||
if (!Timer->IsXboxTimer) {
|
||||
g_AffinityPolicy->SetAffinityOther();
|
||||
}
|
||||
// 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);
|
||||
|
||||
uint64_t NewExpireTime = GetNextExpireTime(Timer);
|
||||
// Always run this thread at dpc level to prevent it from ever executing APCs/DPCs
|
||||
xbox::KeRaiseIrqlToDpcLevel();
|
||||
|
||||
while (true) {
|
||||
if (GetTime_NS(Timer) > NewExpireTime) {
|
||||
if (Timer->Exit.load()) {
|
||||
Timer_Destroy(Timer);
|
||||
return;
|
||||
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;
|
||||
}
|
||||
Timer->Callback(Timer->Opaque);
|
||||
NewExpireTime = GetNextExpireTime(Timer);
|
||||
std::this_thread::yield();
|
||||
}
|
||||
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;
|
||||
|
|
|
@ -40,33 +40,12 @@
|
|||
#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;
|
||||
|
||||
/* 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();
|
||||
|
||||
void timer_init();
|
||||
uint64_t get_now();
|
||||
int64_t Timer_GetScaledPerformanceCounter(int64_t Period);
|
||||
|
||||
void SleepPrecise(std::chrono::steady_clock::time_point targetTime);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -121,9 +121,6 @@ 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();
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
// ******************************************************************
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#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.
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#include <algorithm>
|
||||
#include "Button.h"
|
||||
#include "InputManager.h"
|
||||
#include "layout_xbox_device.h"
|
||||
|
|
|
@ -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,9 +185,18 @@ namespace Libusb
|
|||
|
||||
if (m_Type == XBOX_INPUT_DEVICE::DEVICE_INVALID) { return; }
|
||||
|
||||
// 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) {
|
||||
// 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.
|
||||
libusb_config_descriptor *ConfDesc;
|
||||
if (libusb_get_active_config_descriptor(Dev, &ConfDesc) == 0) {
|
||||
if (ConfDesc->bNumInterfaces == 1) {
|
||||
|
@ -211,7 +220,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) != 0) {
|
||||
if (int err = libusb_claim_interface(m_hDev, m_IfaceNum)) {
|
||||
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;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#define LOG_PREFIX CXBXR_MODULE::SDL
|
||||
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "SdlJoystick.h"
|
||||
|
|
|
@ -26,12 +26,15 @@
|
|||
#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() */
|
||||
|
|
|
@ -1,335 +0,0 @@
|
|||
/*
|
||||
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);
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
#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
|
|
@ -1,42 +0,0 @@
|
|||
#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;
|
||||
}
|
|
@ -27,8 +27,7 @@
|
|||
#ifndef _HASHER_H
|
||||
#define _HASHER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint64_t ComputeHash(const void* data, size_t len);
|
||||
#include "xxhash.h"
|
||||
#define ComputeHash XXH3_64bits
|
||||
|
||||
#endif
|
||||
|
|
|
@ -101,6 +101,10 @@ 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;
|
||||
|
|
|
@ -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(false);
|
||||
CxbxInitWindow();
|
||||
|
||||
ULONG FatalErrorCode = FATAL_ERROR_XBE_DASH_GENERIC;
|
||||
|
||||
|
@ -732,25 +732,29 @@ void Xbe::PurgeBadChar(std::string& s, const std::string& illegalChars)
|
|||
}
|
||||
}
|
||||
|
||||
const char *Xbe::GameRegionToString()
|
||||
const char *Xbe::GameRegionToString(uint32_t dwRegionFlags)
|
||||
{
|
||||
if (!dwRegionFlags) {
|
||||
dwRegionFlags = m_Certificate.dwGameRegion;
|
||||
}
|
||||
|
||||
const char *Region_text[] = {
|
||||
"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)"
|
||||
"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)"
|
||||
};
|
||||
const uint32_t all_regions = XBEIMAGE_GAME_REGION_NA |
|
||||
XBEIMAGE_GAME_REGION_JAPAN |
|
||||
XBEIMAGE_GAME_REGION_RESTOFWORLD |
|
||||
XBEIMAGE_GAME_REGION_MANUFACTURING;
|
||||
|
||||
if(m_Certificate.dwGameRegion & ~all_regions) {
|
||||
if(dwRegionFlags & ~all_regions) {
|
||||
return "REGION ERROR";
|
||||
}
|
||||
|
||||
uint8_t index = (m_Certificate.dwGameRegion & XBEIMAGE_GAME_REGION_MANUFACTURING) ? 0x8 : 0;
|
||||
index |= (m_Certificate.dwGameRegion & 0x7);
|
||||
uint8_t index = (dwRegionFlags & XBEIMAGE_GAME_REGION_MANUFACTURING) ? 0x8 : 0;
|
||||
index |= (dwRegionFlags & 0x7);
|
||||
return Region_text[index];
|
||||
}
|
||||
|
||||
|
@ -825,5 +829,15 @@ 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);
|
||||
|
|
|
@ -81,10 +81,13 @@ class Xbe : public Error
|
|||
void PurgeBadChar(std::string& s, const std::string& illegalChars = "\\/:?\"<>|");
|
||||
|
||||
// Convert game region field to string
|
||||
const char *GameRegionToString();
|
||||
const char *GameRegionToString(uint32_t dwRegionFlags = 0);
|
||||
|
||||
XbeType GetXbeType();
|
||||
|
||||
uint32_t GetDiscVersion();
|
||||
uint32_t GetPatchVersion();
|
||||
|
||||
// Xbe header
|
||||
#include "AlignPrefix1.h"
|
||||
struct Header
|
||||
|
@ -157,7 +160,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 dwDiskNumber; // 0x00A8 - disk number
|
||||
uint32_t dwDiscNumber; // 0x00A8 - disc number
|
||||
uint32_t dwVersion; // 0x00AC - version
|
||||
uint8_t bzLanKey[16]; // 0x00B0 - lan key
|
||||
uint8_t bzSignatureKey[16]; // 0x00C0 - signature key
|
||||
|
|
|
@ -58,6 +58,7 @@ 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)
|
||||
{
|
||||
|
@ -359,8 +360,12 @@ 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 << "Disk Number : 0x" << std::setw(8) << Xbe_certificate->dwDiskNumber << "\n";
|
||||
text << "Version : 1." << std::dec << std::setw(2) << Xbe_certificate->dwVersion << "\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";
|
||||
|
||||
return text.str();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// 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
|
||||
|
@ -54,7 +51,8 @@ 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
|
||||
|
@ -92,10 +90,9 @@ uniform const float FRONTFACE_FACTOR : register(c27); // Note : PSH_XBOX_CONSTA
|
|||
#define PS_FINALCOMBINERSETTING_CLAMP_SUM
|
||||
#endif
|
||||
|
||||
)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(
|
||||
// Hardcoded state will be inserted here
|
||||
// <HARDCODED STATE GOES HERE>
|
||||
// End hardcoded state
|
||||
|
||||
// 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
|
||||
|
@ -173,10 +170,6 @@ R"DELIMITER(
|
|||
// 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)
|
||||
{
|
||||
|
@ -205,20 +198,39 @@ 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 * 255) * 256) + (b * 255)) / 255 // TODO : Verify whether this works at all !
|
||||
|
||||
#define TwoIntoOne(a,b) (((a * 256) + b) * 255)
|
||||
#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(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)
|
||||
#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)
|
||||
|
||||
// Declare one sampler per each {Sampler Type, Texture Stage} combination
|
||||
// TODO : Generate sampler status?
|
||||
|
@ -312,7 +324,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 = Normal3(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
|
||||
/*--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
|
||||
/*---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
|
||||
|
@ -325,6 +337,34 @@ 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)
|
||||
|
@ -358,12 +398,11 @@ 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, xIn.iFog); // color from PSH_XBOX_CONSTANT_FOG, alpha from vertex shader output / pixel shader input
|
||||
fog = float4(c_fog.rgb, saturate(fogFactor)); // color from PSH_XBOX_CONSTANT_FOG, alpha from vertex shader output / pixel shader input
|
||||
|
||||
// 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(
|
||||
// Xbox shader program will be inserted here
|
||||
// <XBOX SHADER PROGRAM GOES HERE>
|
||||
// End Xbox shader program
|
||||
|
||||
// Copy r0.rgba to output
|
||||
PS_OUTPUT xOut;
|
||||
|
@ -372,5 +411,3 @@ R"DELIMITER(
|
|||
|
||||
return xOut;
|
||||
}
|
||||
|
||||
// End of pixel shader footer)DELIMITER" /* This terminates the footer raw string" // */
|
||||
|
|
108
src/core/hle/D3D8/Direct3D9/CxbxVertexShaderPassthrough.hlsl
Normal file
108
src/core/hle/D3D8/Direct3D9/CxbxVertexShaderPassthrough.hlsl
Normal file
|
@ -0,0 +1,108 @@
|
|||
// 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;
|
||||
}
|
|
@ -1,6 +1,3 @@
|
|||
// 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;
|
||||
|
@ -326,40 +323,17 @@ VS_OUTPUT main(const VS_INPUT xIn)
|
|||
// Temp variable for paired VS instruction
|
||||
float4 temp;
|
||||
|
||||
// Xbox shader program)DELIMITER", /* This terminates the header raw string" // */
|
||||
|
||||
R"DELIMITER(
|
||||
// Xbox shader program will be inserted here
|
||||
// <XBOX SHADER PROGRAM GOES HERE>
|
||||
// End Xbox shader program
|
||||
|
||||
// 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 = fogFactor; // Note : Xbox clamps fog in pixel shader -> *NEEDS TESTING* /was oFog.x
|
||||
xOut.oFog = oFog.x; // Note : Xbox clamps fog in pixel shader -> *NEEDS TESTING* /was oFog.x
|
||||
xOut.oPts = oPts.x;
|
||||
xOut.oB0 = saturate(oB0);
|
||||
xOut.oB1 = saturate(oB1);
|
||||
|
@ -371,5 +345,3 @@ R"DELIMITER(
|
|||
|
||||
return xOut;
|
||||
}
|
||||
|
||||
// End of vertex shader footer)DELIMITER" /* This terminates the footer raw string" // */
|
||||
|
|
|
@ -38,10 +38,12 @@
|
|||
#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
|
||||
|
@ -62,7 +64,7 @@
|
|||
#include "common\input\DInputKeyboardMouse.h"
|
||||
#include "common\input\InputManager.h"
|
||||
#include "common/util/strConverter.hpp" // for utf8_to_utf16
|
||||
#include "VertexShaderSource.h"
|
||||
#include "VertexShaderCache.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#include <imgui.h>
|
||||
|
@ -159,8 +161,6 @@ 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,7 +168,6 @@ 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;
|
||||
|
@ -228,7 +227,6 @@ 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);
|
||||
|
@ -320,15 +318,17 @@ 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, () );*/\
|
||||
/*XB_MACRO(xbox::void_xt, __stdcall, D3DDevice_SelectVertexShader_4, (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, 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, () ); \
|
||||
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetPixelShader_0__LTCG_eax_handle, () ); \
|
||||
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, (xbox::X_D3DBaseTexture*) ); \
|
||||
XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetTexture_4__LTCG_eax_Stage, (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,25 +626,13 @@ const char *D3DErrorString(HRESULT hResult)
|
|||
return buffer;
|
||||
}
|
||||
|
||||
void CxbxInitWindow(bool bFullInit)
|
||||
void CxbxInitWindow()
|
||||
{
|
||||
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
|
||||
{
|
||||
|
@ -680,6 +668,10 @@ void CxbxInitWindow(bool bFullInit)
|
|||
g_renderbase->SetWindowRelease([] {
|
||||
ImGui_ImplWin32_Shutdown();
|
||||
});
|
||||
|
||||
(void) g_ShaderSources.Update();
|
||||
g_ShaderSources.InitShaderHotloading();
|
||||
|
||||
}
|
||||
|
||||
void DrawUEM(HWND hWnd)
|
||||
|
@ -1699,6 +1691,13 @@ 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;
|
||||
}
|
||||
|
@ -1716,6 +1715,14 @@ 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)
|
||||
|
@ -1923,47 +1930,25 @@ std::chrono::steady_clock::time_point GetNextVBlankTime()
|
|||
return steady_clock::now() + duration_cast<steady_clock::duration>(ms);
|
||||
}
|
||||
|
||||
// timing thread procedure
|
||||
static xbox::void_xt NTAPI EmuUpdateTickCount(xbox::PVOID Arg)
|
||||
void hle_vblank()
|
||||
{
|
||||
CxbxSetThreadName("Cxbx Timing Thread");
|
||||
// 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++;
|
||||
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Timing thread is running.");
|
||||
// TODO: Fixme. This may not be right...
|
||||
g_Xbox_SwapData.SwapVBlank = 1;
|
||||
|
||||
auto nextVBlankTime = GetNextVBlankTime();
|
||||
g_Xbox_VBlankData.Swap = 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: This can't be accurate...
|
||||
g_Xbox_SwapData.TimeUntilSwapVBlank = 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;
|
||||
}
|
||||
// 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)
|
||||
|
@ -2271,7 +2256,7 @@ static void CreateDefaultD3D9Device
|
|||
DrawInitialBlackScreen();
|
||||
|
||||
// Set up cache
|
||||
g_VertexShaderSource.ResetD3DDevice(g_pD3DDevice);
|
||||
g_VertexShaderCache.ResetD3DDevice(g_pD3DDevice);
|
||||
|
||||
// Set up ImGui's render backend
|
||||
ImGui_ImplDX9_Init(g_pD3DDevice);
|
||||
|
@ -2813,10 +2798,13 @@ void Direct3D_CreateDevice_End
|
|||
{
|
||||
#if 0 // Unused :
|
||||
// Set g_Xbox_D3DDevice to point to the Xbox D3D Device
|
||||
auto it = g_SymbolAddresses.find("D3DDEVICE");
|
||||
auto it = g_SymbolAddresses.find("D3D_g_pDevice");
|
||||
if (it != g_SymbolAddresses.end()) {
|
||||
g_Xbox_D3DDevice = (DWORD*)it->second;
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "D3D_g_pDevice was not found!");
|
||||
}
|
||||
#endif
|
||||
|
||||
UpdateHostBackBufferDesc();
|
||||
|
@ -3153,6 +3141,12 @@ 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);
|
||||
|
||||
|
@ -3164,30 +3158,41 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(Direct3D_CreateDevice)
|
|||
// ******************************************************************
|
||||
// * patch: D3DDevice_Reset
|
||||
// ******************************************************************
|
||||
xbox::hresult_xt WINAPI xbox::EMUPATCH(D3DDevice_Reset)
|
||||
(
|
||||
X_D3DPRESENT_PARAMETERS* pPresentationParameters
|
||||
)
|
||||
{
|
||||
LOG_FUNC_ONE_ARG(pPresentationParameters)
|
||||
|
||||
static void CxbxImpl_Reset(xbox::X_D3DPRESENT_PARAMETERS* 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);
|
||||
|
||||
|
@ -3198,6 +3203,70 @@ 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
|
||||
|
@ -3237,12 +3306,12 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_GetDisplayFieldStatus)(X_D3DFIELD_
|
|||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_BeginPush
|
||||
// * patch: D3DDevice_BeginPush_4
|
||||
// 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)(dword_xt Count)
|
||||
xbox::PDWORD WINAPI xbox::EMUPATCH(D3DDevice_BeginPush_4)(dword_xt Count)
|
||||
{
|
||||
LOG_FUNC_ONE_ARG(Count);
|
||||
|
||||
|
@ -3260,11 +3329,11 @@ xbox::PDWORD WINAPI xbox::EMUPATCH(D3DDevice_BeginPush)(dword_xt Count)
|
|||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_BeginPush2
|
||||
// * patch: D3DDevice_BeginPush_8
|
||||
// TODO: Find a test case and verify this: RalliSport Challenge XDK 4134
|
||||
// For XDK before 4531
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_BeginPush2)(dword_xt Count, dword_xt** ppPush)
|
||||
xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_BeginPush_8)(dword_xt Count, dword_xt** ppPush)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Count)
|
||||
|
@ -3587,7 +3656,7 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_LoadVertexShader)
|
|||
}
|
||||
|
||||
// Overload for logging
|
||||
static void D3DDevice_SelectVertexShader_0
|
||||
static void D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2
|
||||
(
|
||||
xbox::dword_xt Handle,
|
||||
xbox::dword_xt Address
|
||||
|
@ -3602,7 +3671,7 @@ static void D3DDevice_SelectVertexShader_0
|
|||
// 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)
|
||||
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2)
|
||||
(
|
||||
)
|
||||
{
|
||||
|
@ -3615,7 +3684,7 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SelectVertexShad
|
|||
}
|
||||
|
||||
// Log
|
||||
D3DDevice_SelectVertexShader_0(Handle, Address);
|
||||
D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2(Handle, Address);
|
||||
|
||||
CxbxImpl_SelectVertexShader(Handle, Address);
|
||||
|
||||
|
@ -3626,7 +3695,7 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SelectVertexShad
|
|||
}
|
||||
|
||||
// Overload for logging
|
||||
static void D3DDevice_SelectVertexShader_4
|
||||
static void D3DDevice_SelectVertexShader_4__LTCG_eax1
|
||||
(
|
||||
xbox::dword_xt Handle,
|
||||
xbox::dword_xt Address
|
||||
|
@ -3641,7 +3710,7 @@ static void D3DDevice_SelectVertexShader_4
|
|||
// 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)
|
||||
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SelectVertexShader_4__LTCG_eax1)
|
||||
(
|
||||
dword_xt Address
|
||||
)
|
||||
|
@ -3653,7 +3722,7 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SelectVertexShad
|
|||
}
|
||||
|
||||
// Log
|
||||
D3DDevice_SelectVertexShader_4(Handle, Address);
|
||||
D3DDevice_SelectVertexShader_4__LTCG_eax1(Handle, Address);
|
||||
|
||||
CxbxImpl_SelectVertexShader(Handle, Address);
|
||||
|
||||
|
@ -4371,7 +4440,7 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTexture_4__LT
|
|||
}
|
||||
|
||||
// Overload for logging
|
||||
static void D3DDevice_SetTexture_4
|
||||
static void D3DDevice_SetTexture_4__LTCG_eax_Stage
|
||||
(
|
||||
xbox::dword_xt Stage,
|
||||
xbox::X_D3DBaseTexture *pTexture
|
||||
|
@ -4386,7 +4455,7 @@ static void D3DDevice_SetTexture_4
|
|||
// 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)
|
||||
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_Stage)
|
||||
(
|
||||
X_D3DBaseTexture *pTexture
|
||||
)
|
||||
|
@ -4398,13 +4467,13 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTexture_4)
|
|||
}
|
||||
|
||||
// Log
|
||||
D3DDevice_SetTexture_4(Stage, pTexture);
|
||||
D3DDevice_SetTexture_4__LTCG_eax_Stage(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)
|
||||
call XB_TRMP(D3DDevice_SetTexture_4__LTCG_eax_Stage)
|
||||
}
|
||||
|
||||
g_pXbox_SetTexture[Stage] = pTexture;
|
||||
|
@ -6456,31 +6525,6 @@ 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
|
||||
// ******************************************************************
|
||||
|
@ -7452,11 +7496,11 @@ void CxbxUpdateHostVertexShaderConstants()
|
|||
}
|
||||
|
||||
// Placed this here until we find a better place
|
||||
const uint32_t fogTableMode = XboxRenderStates.GetXboxRenderState(xbox::_X_D3DRENDERSTATETYPE::X_D3DRS_FOGTABLEMODE);
|
||||
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);
|
||||
float fogStuff[4] = { (float)fogTableMode, fogDensity, fogStart, fogEnd };
|
||||
float fogStuff[4] = {fogTableMode, fogDensity, fogStart, fogEnd};
|
||||
g_pD3DDevice->SetVertexShaderConstantF(CXBX_D3DVS_CONSTREG_FOGINFO, fogStuff, 1);
|
||||
}
|
||||
|
||||
|
@ -7712,7 +7756,7 @@ xbox::void_xt CxbxImpl_SetPixelShader(xbox::dword_xt Handle)
|
|||
}
|
||||
|
||||
// Overload for logging
|
||||
static void D3DDevice_SetPixelShader_0
|
||||
static void D3DDevice_SetPixelShader_0__LTCG_eax_handle
|
||||
(
|
||||
xbox::dword_xt Handle
|
||||
)
|
||||
|
@ -7725,7 +7769,7 @@ static void D3DDevice_SetPixelShader_0
|
|||
// 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)()
|
||||
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetPixelShader_0__LTCG_eax_handle)()
|
||||
{
|
||||
dword_xt Handle;
|
||||
__asm {
|
||||
|
@ -7734,11 +7778,11 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetPixelShader_0
|
|||
}
|
||||
|
||||
// Log
|
||||
D3DDevice_SetPixelShader_0(Handle);
|
||||
D3DDevice_SetPixelShader_0__LTCG_eax_handle(Handle);
|
||||
|
||||
__asm {
|
||||
mov eax, Handle
|
||||
call XB_TRMP(D3DDevice_SetPixelShader_0)
|
||||
call XB_TRMP(D3DDevice_SetPixelShader_0__LTCG_eax_handle)
|
||||
}
|
||||
|
||||
CxbxImpl_SetPixelShader(Handle);
|
||||
|
@ -7771,7 +7815,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)
|
||||
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawVertices_4__LTCG_ecx2_eax3)
|
||||
(
|
||||
X_D3DPRIMITIVETYPE PrimitiveType
|
||||
)
|
||||
|
@ -7792,6 +7836,30 @@ __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
|
||||
// ******************************************************************
|
||||
|
@ -7815,7 +7883,7 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawVertices)
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO : Call unpatched CDevice_SetStateVB(0);
|
||||
// TODO : Call unpatched CDevice_SetStateVB[_8](0);
|
||||
|
||||
CxbxUpdateNativeD3DResources();
|
||||
|
||||
|
@ -7953,7 +8021,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)
|
||||
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawVerticesUP_12__LTCG_ebx3)
|
||||
(
|
||||
X_D3DPRIMITIVETYPE PrimitiveType,
|
||||
uint_xt VertexCount,
|
||||
|
@ -7999,7 +8067,7 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_DrawIndexedVertices)
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO : Call unpatched CDevice_SetStateVB(g_Xbox_BaseVertexIndex);
|
||||
// TODO : Call unpatched CDevice_SetStateVB[_8](g_Xbox_BaseVertexIndex);
|
||||
|
||||
CxbxUpdateNativeD3DResources();
|
||||
|
||||
|
@ -9008,28 +9076,84 @@ 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)
|
||||
{
|
||||
LOG_FUNC_ONE_ARG(Unknown1);
|
||||
addr_xt _this;
|
||||
__asm mov _this, ecx;
|
||||
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(_this)
|
||||
LOG_FUNC_ARG(Unknown1)
|
||||
LOG_FUNC_END;
|
||||
|
||||
// TODO: Anything?
|
||||
// __asm int 3;
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
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();
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: CDevice_SetStateUP (D3D::CDevice::SetStateUP)
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI xbox::EMUPATCH(CDevice_SetStateUP)()
|
||||
xbox::void_xt CxbxrImpl_CDevice_SetStateUP(xbox::addr_xt _this)
|
||||
{
|
||||
LOG_FUNC();
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
// TODO: Anything?
|
||||
// __asm int 3;
|
||||
|
||||
//__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
|
||||
}
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -4256,3 +4256,27 @@ 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;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
void LookupTrampolinesD3D();
|
||||
|
||||
// initialize render window
|
||||
extern void CxbxInitWindow(bool bFullInit);
|
||||
extern void CxbxInitWindow();
|
||||
|
||||
void CxbxUpdateNativeD3DResources();
|
||||
|
||||
|
@ -144,14 +144,14 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_GetDisplayFieldStatus)
|
|||
);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_BeginPush
|
||||
// * patch: D3DDevice_BeginPush_4
|
||||
// ******************************************************************
|
||||
xbox::PDWORD WINAPI EMUPATCH(D3DDevice_BeginPush)(dword_xt Count);
|
||||
xbox::PDWORD WINAPI EMUPATCH(D3DDevice_BeginPush_4)(dword_xt Count);
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_BeginPush2 //two arg version for xdk before 4531
|
||||
// * patch: D3DDevice_BeginPush_8
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_BeginPush2)(dword_xt Count, dword_xt **ppPush);
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_BeginPush_8)(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)();
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_4)
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2)();
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShader_4__LTCG_eax1)
|
||||
(
|
||||
dword_xt Address
|
||||
);
|
||||
|
@ -359,6 +359,10 @@ 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
|
||||
// ******************************************************************
|
||||
|
@ -505,7 +509,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader)
|
|||
dword_xt Handle
|
||||
);
|
||||
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader_0)();
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader_0__LTCG_eax_handle)();
|
||||
|
||||
// ******************************************************************
|
||||
// * patch: D3DDevice_CreateTexture2
|
||||
|
@ -611,7 +615,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_pTexture)
|
|||
dword_xt Stage
|
||||
);
|
||||
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4)
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_Stage)
|
||||
(
|
||||
X_D3DBaseTexture *pTexture
|
||||
);
|
||||
|
@ -1390,18 +1394,27 @@ 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
|
||||
// * patch: D3DDevice_DrawVertices_4__LTCG_ecx2_eax3
|
||||
// ******************************************************************
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices_4)
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVertices_4__LTCG_ecx2_eax3)
|
||||
(
|
||||
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
|
||||
// ******************************************************************
|
||||
|
@ -1413,11 +1426,11 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVerticesUP)
|
|||
uint_xt VertexStreamZeroStride
|
||||
);
|
||||
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVerticesUP_12)
|
||||
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DrawVerticesUP_12__LTCG_ebx3)
|
||||
(
|
||||
X_D3DPRIMITIVETYPE PrimitiveType,
|
||||
uint_xt VertexCount,
|
||||
uint_xt VertexStreamZeroStride
|
||||
uint_xt VertexCount,
|
||||
uint_xt VertexStreamZeroStride
|
||||
);
|
||||
|
||||
|
||||
|
@ -1897,12 +1910,15 @@ 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)(xbox::ulong_xt Unknown1);
|
||||
xbox::void_xt WINAPI EMUPATCH(CDevice_SetStateVB_8)(xbox::addr_xt _this, 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
|
||||
|
|
|
@ -237,6 +237,24 @@ 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
|
||||
|
@ -284,7 +302,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(input.iFog));
|
||||
ctx.CURRENT.rgb = lerp(state.FogColor.rgb, ctx.CURRENT.rgb, saturate(fogFactor));
|
||||
}
|
||||
|
||||
// Add specular if enabled
|
||||
|
|
|
@ -63,10 +63,16 @@ 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;
|
||||
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;
|
||||
|
||||
// This state is passed to the shader
|
||||
struct PsTextureStageState {
|
||||
|
@ -125,7 +131,11 @@ 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
|
||||
|
|
|
@ -288,20 +288,8 @@ float DoFog(const VS_INPUT xIn)
|
|||
fogDepth = abs(Projection.Position.z);
|
||||
if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_W)
|
||||
fogDepth = Projection.Position.w;
|
||||
|
||||
// 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;
|
||||
|
||||
return fogDepth;
|
||||
}
|
||||
|
||||
float4 DoTexCoord(const uint stage, const VS_INPUT xIn)
|
||||
|
@ -351,9 +339,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(1, 0, 0);
|
||||
texCoord.yzw = float3(0, 0, 1);
|
||||
if (componentCount == 2)
|
||||
texCoord.zw = float2(1, 0);
|
||||
texCoord.zw = float2(0, 1);
|
||||
if (componentCount == 3)
|
||||
texCoord.w = 1;
|
||||
} // Generate texture coordinates
|
||||
|
@ -390,14 +378,15 @@ 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.yzw = texCoord.x;
|
||||
if (countFlag == 2)
|
||||
texCoord.zw = texCoord.y;
|
||||
if (countFlag == 3)
|
||||
texCoord.w = texCoord.z;
|
||||
//if (countFlag == 1)
|
||||
//texCoord.yz = texCoord.x;
|
||||
//if (countFlag == 2)
|
||||
//texCoord.z = texCoord.y;
|
||||
//texCoord.xyzw = texCoord.xyzw / texCoord.w;
|
||||
}
|
||||
|
||||
return texCoord;
|
||||
|
|
|
@ -289,12 +289,7 @@ bool IsTextureSampled(DecodedRegisterCombiner* pShader, int reg)
|
|||
|
||||
void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
|
||||
{
|
||||
// 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 << g_ShaderSources.pixelShaderTemplateHlsl[0]; // Start with the HLSL template header
|
||||
|
||||
hlsl << "\n#define ALPHAKILL {"
|
||||
<< (pShader->AlphaKill[0] ? "true, " : "false, ")
|
||||
|
@ -341,9 +336,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 << hlsl_template[1];
|
||||
hlsl << hlsl_template[2];
|
||||
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[1];
|
||||
|
||||
// Generate all four texture stages
|
||||
for (unsigned i = 0; i < PSH_XBOX_MAX_T_REGISTER_COUNT; i++) {
|
||||
|
@ -390,7 +385,7 @@ void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
|
|||
|
||||
FinalCombinerStageHlsl(hlsl, pShader->FinalCombiner, pShader->hasFinalCombiner);
|
||||
|
||||
hlsl << hlsl_template[3]; // Finish with the HLSL template footer
|
||||
hlsl << g_ShaderSources.pixelShaderTemplateHlsl[2]; // Finish with the HLSL template footer
|
||||
}
|
||||
|
||||
// recompile xbox pixel shader function
|
||||
|
|
|
@ -36,20 +36,14 @@ void SetXboxMultiSampleType(xbox::X_D3DMULTISAMPLE_TYPE value);
|
|||
|
||||
bool XboxRenderStateConverter::Init()
|
||||
{
|
||||
if (g_SymbolAddresses.find("D3DDeferredRenderState") != g_SymbolAddresses.end()) {
|
||||
D3D__RenderState = (uint32_t*)g_SymbolAddresses["D3DDeferredRenderState"];
|
||||
// Get render state
|
||||
if (g_SymbolAddresses.find("D3D_g_RenderState") != g_SymbolAddresses.end()) {
|
||||
D3D__RenderState = (uint32_t*)g_SymbolAddresses["D3D_g_RenderState"];
|
||||
} 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();
|
||||
|
||||
|
@ -70,54 +64,6 @@ 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");
|
||||
|
|
|
@ -29,10 +29,18 @@
|
|||
|
||||
#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();
|
||||
|
@ -140,3 +148,173 @@ 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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <string> // std::string
|
||||
#include <d3dcompiler.h> // ID3DBlob (via d3d9.h > d3d11shader.h > d3dcommon.h)
|
||||
|
||||
|
@ -10,3 +11,38 @@ 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;
|
||||
|
|
|
@ -79,11 +79,12 @@ TextureStateInfo CxbxTextureStateInfo[] = {
|
|||
|
||||
bool XboxTextureStateConverter::Init(XboxRenderStateConverter* pState)
|
||||
{
|
||||
// Deferred states start at 0, this means that D3DDeferredTextureState IS D3D__TextureState
|
||||
// Deferred states start at 0, this means that D3D_g_DeferredTextureState IS D3D__TextureState
|
||||
// No further works is required to derive the offset
|
||||
if (g_SymbolAddresses.find("D3DDeferredTextureState") != g_SymbolAddresses.end()) {
|
||||
D3D__TextureState = (uint32_t*)g_SymbolAddresses["D3DDeferredTextureState"];
|
||||
if (g_SymbolAddresses.find("D3D_g_DeferredTextureState") != g_SymbolAddresses.end()) {
|
||||
D3D__TextureState = (uint32_t*)g_SymbolAddresses["D3D_g_DeferredTextureState"];
|
||||
} else {
|
||||
EmuLog(LOG_LEVEL::ERROR2, "D3D_g_DeferredTextureState was not found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
#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;
|
||||
|
@ -290,26 +288,23 @@ extern HRESULT EmuCompileVertexShader
|
|||
ID3DBlob** ppHostShader
|
||||
)
|
||||
{
|
||||
// Include HLSL header and footer as raw strings :
|
||||
static std::string hlsl_template[2] = {
|
||||
#include "core\hle\D3D8\Direct3D9\CxbxVertexShaderTemplate.hlsl"
|
||||
};
|
||||
|
||||
auto hlsl_stream = std::stringstream();
|
||||
hlsl_stream << hlsl_template[0]; // Start with the HLSL template header
|
||||
assert(pIntermediateShader->Instructions.size() > 0);
|
||||
BuildShader(pIntermediateShader, hlsl_stream);
|
||||
|
||||
hlsl_stream << hlsl_template[1]; // Finish with the HLSL template footer
|
||||
// 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
|
||||
BuildShader(pIntermediateShader, hlsl_stream);
|
||||
hlsl_stream << g_ShaderSources.vertexShaderTemplateHlsl[1]; // Finish with the HLSL template footer
|
||||
std::string hlsl_str = hlsl_stream.str();
|
||||
|
||||
HRESULT hRet = EmuCompileShader(hlsl_str, g_vs_model, ppHostShader, "CxbxVertexShaderTemplate.hlsl");
|
||||
const char* notionalSourceName = "CxbxVertexShaderTemplate.hlsl";
|
||||
HRESULT hRet = EmuCompileShader(hlsl_str, g_vs_model, ppHostShader, notionalSourceName);
|
||||
|
||||
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, "CxbxVertexShaderTemplate.hlsl");
|
||||
hRet = EmuCompileShader(hlsl_str, vs_model_3_0, ppHostShader, notionalSourceName);
|
||||
}
|
||||
|
||||
return hRet;
|
||||
|
@ -317,174 +312,10 @@ extern HRESULT EmuCompileVertexShader
|
|||
|
||||
extern void EmuCompileFixedFunction(ID3DBlob** ppHostShader)
|
||||
{
|
||||
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;
|
||||
EmuCompileShader(g_ShaderSources.fixedFunctionVertexShaderHlsl, g_vs_model, ppHostShader, g_ShaderSources.fixedFunctionVertexShaderPath.c_str());
|
||||
};
|
||||
|
||||
static ID3DBlob* pPassthroughShader = nullptr;
|
||||
|
||||
extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader)
|
||||
extern void EmuCompileXboxPassthrough(ID3DBlob** ppHostShader)
|
||||
{
|
||||
// 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;
|
||||
EmuCompileShader(g_ShaderSources.vertexShaderPassthroughHlsl, g_vs_model, ppHostShader, g_ShaderSources.vertexShaderPassthroughPath.c_str());
|
||||
}
|
||||
|
|
|
@ -21,5 +21,5 @@ extern HRESULT EmuCompileVertexShader
|
|||
|
||||
extern void EmuCompileFixedFunction(ID3DBlob** ppHostShader);
|
||||
|
||||
extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader);
|
||||
extern void EmuCompileXboxPassthrough(ID3DBlob** ppHostShader);
|
||||
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
#define LOG_PREFIX CXBXR_MODULE::VSHCACHE
|
||||
|
||||
#include "VertexShaderSource.h"
|
||||
#include "VertexShaderCache.h"
|
||||
|
||||
#include "core/kernel/init/CxbxKrnl.h"
|
||||
#include "util/hasher.h"
|
||||
#include "core/kernel/support/Emu.h"
|
||||
|
||||
VertexShaderSource g_VertexShaderSource = VertexShaderSource();
|
||||
VertexShaderCache g_VertexShaderCache = VertexShaderCache();
|
||||
// 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;
|
||||
|
||||
|
@ -25,7 +26,7 @@ ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, S
|
|||
|
||||
// Find a shader
|
||||
// Return true if the shader was found
|
||||
bool VertexShaderSource::_FindShader(ShaderKey key, LazyVertexShader** ppLazyShader) {
|
||||
bool VertexShaderCache::_FindShader(ShaderKey key, LazyVertexShader** ppLazyShader) {
|
||||
auto it = cache.find(key);
|
||||
if (it == cache.end()) {
|
||||
// We didn't find anything! Was CreateShader called?
|
||||
|
@ -39,7 +40,7 @@ ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, S
|
|||
|
||||
// Create a new shader
|
||||
// If the shader was already created, just increase its reference count
|
||||
ShaderKey VertexShaderSource::CreateShader(const xbox::dword_xt* pXboxFunction, DWORD *pXboxFunctionSize) {
|
||||
ShaderKey VertexShaderCache::CreateShader(const xbox::dword_xt* pXboxFunction, DWORD *pXboxFunctionSize) {
|
||||
IntermediateVertexShader intermediateShader;
|
||||
|
||||
*pXboxFunctionSize = GetVshFunctionSize(pXboxFunction);
|
||||
|
@ -86,7 +87,7 @@ ShaderKey VertexShaderSource::CreateShader(const xbox::dword_xt* pXboxFunction,
|
|||
}
|
||||
|
||||
// Get a shader using the given key
|
||||
IDirect3DVertexShader* VertexShaderSource::GetShader(ShaderKey key)
|
||||
IDirect3DVertexShader* VertexShaderCache::GetShader(ShaderKey key)
|
||||
{
|
||||
LazyVertexShader* pLazyShader = nullptr;
|
||||
|
||||
|
@ -113,6 +114,12 @@ IDirect3DVertexShader* VertexShaderSource::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
|
||||
(
|
||||
|
@ -145,7 +152,7 @@ IDirect3DVertexShader* VertexShaderSource::GetShader(ShaderKey key)
|
|||
}
|
||||
|
||||
// Release a shader. Doesn't actually release any resources for now
|
||||
void VertexShaderSource::ReleaseShader(ShaderKey key)
|
||||
void VertexShaderCache::ReleaseShader(ShaderKey key)
|
||||
{
|
||||
// For now, don't bother releasing any shaders
|
||||
LazyVertexShader* pLazyShader;
|
||||
|
@ -165,8 +172,25 @@ void VertexShaderSource::ReleaseShader(ShaderKey key)
|
|||
}
|
||||
}
|
||||
|
||||
void VertexShaderSource::ResetD3DDevice(IDirect3DDevice9* newDevice)
|
||||
void VertexShaderCache::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();
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
typedef uint64_t ShaderKey;
|
||||
|
||||
// Manages creation and caching of vertex shaders
|
||||
class VertexShaderSource {
|
||||
class VertexShaderCache {
|
||||
|
||||
public:
|
||||
ShaderKey CreateShader(const xbox::dword_xt* pXboxFunction, DWORD* pXboxFunctionSize);
|
||||
|
@ -16,6 +16,7 @@ public:
|
|||
void ReleaseShader(ShaderKey key);
|
||||
|
||||
void ResetD3DDevice(IDirect3DDevice9* pD3DDevice);
|
||||
void Clear();
|
||||
|
||||
// TODO
|
||||
// WriteCacheToDisk
|
||||
|
@ -42,6 +43,6 @@ private:
|
|||
bool _FindShader(ShaderKey key, LazyVertexShader** ppLazyShader);
|
||||
};
|
||||
|
||||
extern VertexShaderSource g_VertexShaderSource;
|
||||
extern VertexShaderCache g_VertexShaderCache;
|
||||
|
||||
#endif
|
|
@ -39,6 +39,7 @@
|
|||
|
||||
#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()
|
||||
|
@ -51,6 +52,7 @@
|
|||
#include <assert.h> // assert()
|
||||
#include <process.h>
|
||||
#include <locale.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
|
@ -659,35 +661,12 @@ 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_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;
|
||||
}
|
||||
constexpr int PSH_XBOX_CONSTANT_MAX = PSH_XBOX_CONSTANT_FOGENABLE + 1; // = 30
|
||||
|
||||
std::string_view GetD3DTOPString(int d3dtop) {
|
||||
static constexpr std::string_view opToString[] = {
|
||||
|
@ -789,6 +768,21 @@ 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 };
|
||||
|
@ -871,68 +865,74 @@ IDirect3DPixelShader9* GetFixedFunctionShader()
|
|||
}
|
||||
|
||||
// Build and compile a new shader
|
||||
auto hlslTemplate = GetFixedFunctionShaderTemplate();
|
||||
std::string hlslTemplate = g_ShaderSources.fixedFunctionPixelShaderHlsl;
|
||||
|
||||
// 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;
|
||||
|
||||
static constexpr std::string_view typeToString[] = {
|
||||
"SAMPLE_NONE",
|
||||
"SAMPLE_2D",
|
||||
"SAMPLE_3D",
|
||||
"SAMPLE_CUBE"
|
||||
};
|
||||
if (sampleTypeReplace != std::string::npos) {
|
||||
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]] << "};";
|
||||
|
||||
auto finalShader = hlslTemplate.replace(sampleTypeReplace, sampleTypePattern.size(), sampleTypeString.str());
|
||||
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) + stageDef.size();
|
||||
auto stageDefInsert = finalShader.find(stageDef);
|
||||
if (stageDefInsert != std::string::npos) {
|
||||
stageDefInsert += 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';
|
||||
stageSetup << target << "RESULTARG = " << GetD3DTASumString(s.RESULTARG, false) << ";\n";
|
||||
stageSetup << '\n';
|
||||
}
|
||||
|
||||
finalShader = finalShader.insert(stageDefInsert, stageSetup.str());
|
||||
}
|
||||
|
||||
finalShader = finalShader.insert(stageDefInsert, stageSetup.str());
|
||||
|
||||
// Compile the shader
|
||||
ID3DBlob* pShaderBlob;
|
||||
|
||||
|
@ -944,12 +944,15 @@ 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;
|
||||
auto hRet = g_pD3DDevice->CreatePixelShader((DWORD*)pShaderBlob->GetBufferPointer(), &pShader);
|
||||
if (hRet != S_OK)
|
||||
CxbxrAbort("Failed to compile fixed function pixel shader");
|
||||
pShaderBlob->Release();
|
||||
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();
|
||||
}
|
||||
|
||||
// Insert the shader into the cache
|
||||
ffPsCache[key] = pShader;
|
||||
|
@ -972,7 +975,10 @@ 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++) {
|
||||
|
||||
|
@ -1029,6 +1035,21 @@ 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;
|
||||
|
@ -1150,7 +1171,18 @@ 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:
|
||||
|
|
|
@ -469,12 +469,15 @@ 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);
|
||||
return; // For now, don't even attempt to run through
|
||||
// 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!");
|
||||
}
|
||||
else {
|
||||
LOG_TEST_CASE("Pushbuffer COMMAND_TYPE_CALL");
|
||||
|
|
|
@ -34,7 +34,8 @@
|
|||
#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\VertexShaderSource.h" // For g_VertexShaderSource
|
||||
#include "core\hle\D3D8\Direct3D9\VertexShaderCache.h" // For g_VertexShaderCache
|
||||
#include "core\hle\D3D8\Direct3D9\Shader.h" // For g_ShaderSources
|
||||
#include "core\hle\D3D8\XbVertexBuffer.h" // For CxbxImpl_SetVertexData4f
|
||||
#include "core\hle\D3D8\XbVertexShader.h"
|
||||
#include "core\hle\D3D8\XbD3D8Logging.h" // For DEBUG_D3DRESULT
|
||||
|
@ -49,6 +50,7 @@
|
|||
#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
|
||||
|
@ -1124,10 +1126,49 @@ IDirect3DVertexDeclaration* CxbxCreateHostVertexDeclaration(D3DVERTEXELEMENT *pD
|
|||
return pHostVertexDeclaration;
|
||||
}
|
||||
|
||||
static IDirect3DVertexShader* passthroughshader;
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -1135,43 +1176,20 @@ void CxbxUpdateHostVertexShader()
|
|||
LOG_INIT; // Allows use of DEBUG_D3DRESULT
|
||||
|
||||
if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction) {
|
||||
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);
|
||||
HRESULT hRet = g_pD3DDevice->SetVertexShader(fixedFunctionShader);
|
||||
if (FAILED(hRet)) CxbxrAbort("Failed to set fixed-function shader");
|
||||
}
|
||||
else if (g_Xbox_VertexShaderMode == VertexShaderMode::Passthrough && g_bUsePassthroughHLSL) {
|
||||
if (passthroughshader == nullptr) {
|
||||
ID3DBlob* pBlob = nullptr;
|
||||
EmuCompileXboxPassthrough(&pBlob);
|
||||
if (pBlob) {
|
||||
g_pD3DDevice->CreateVertexShader((DWORD*)pBlob->GetBufferPointer(), &passthroughshader);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT hRet = g_pD3DDevice->SetVertexShader(passthroughshader);
|
||||
HRESULT hRet = g_pD3DDevice->SetVertexShader(passthroughShader);
|
||||
if (FAILED(hRet)) CxbxrAbort("Failed to set passthrough shader");
|
||||
}
|
||||
else {
|
||||
auto pTokens = GetCxbxVertexShaderSlotPtr(g_Xbox_VertexShader_FunctionSlots_StartAddress);
|
||||
assert(pTokens);
|
||||
// Create a vertex shader from the tokens
|
||||
DWORD shaderSize;
|
||||
auto VertexShaderKey = g_VertexShaderSource.CreateShader(pTokens, &shaderSize);
|
||||
IDirect3DVertexShader* pHostVertexShader = g_VertexShaderSource.GetShader(VertexShaderKey);
|
||||
auto VertexShaderKey = g_VertexShaderCache.CreateShader(pTokens, &shaderSize);
|
||||
IDirect3DVertexShader* pHostVertexShader = g_VertexShaderCache.GetShader(VertexShaderKey);
|
||||
HRESULT hRet = g_pD3DDevice->SetVertexShader(pHostVertexShader);
|
||||
DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader");
|
||||
}
|
||||
|
@ -1559,7 +1577,7 @@ void CxbxImpl_DeleteVertexShader(DWORD Handle)
|
|||
RegisterCxbxVertexDeclaration(pCxbxVertexDeclaration->Key, nullptr); // Remove from cache (which will free present pCxbxVertexDeclaration)
|
||||
|
||||
// Release the host vertex shader
|
||||
g_VertexShaderSource.ReleaseShader(pCxbxVertexShader->Key);
|
||||
g_VertexShaderCache.ReleaseShader(pCxbxVertexShader->Key);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
// 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()
|
||||
{
|
||||
|
@ -85,10 +86,6 @@ 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
|
||||
|
@ -98,6 +95,7 @@ extern "C" {
|
|||
void CxbxInitAudio()
|
||||
{
|
||||
g_EmuShared->GetAudioSettings(&g_XBAudio);
|
||||
dsound_last = get_now();
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -125,10 +123,6 @@ 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;
|
||||
|
||||
|
@ -454,51 +448,51 @@ void StreamBufferAudio(xbox::XbHybridDSBuffer* pHybridBuffer, float msToCopy) {
|
|||
}
|
||||
}
|
||||
|
||||
static void dsound_thread_worker(LPVOID nullPtr)
|
||||
void dsound_async_worker()
|
||||
{
|
||||
g_AffinityPolicy->SetAffinityOther();
|
||||
DSoundMutexGuardLock;
|
||||
|
||||
const int dsStreamInterval = 300;
|
||||
int waitCounter = 0;
|
||||
xbox::LARGE_INTEGER getTime;
|
||||
xbox::KeQuerySystemTime(&getTime);
|
||||
DirectSoundDoWork_Stream(getTime);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
// FIXME time this loop more accurately
|
||||
// and account for variation in the length of Sleep calls
|
||||
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.
|
||||
|
||||
// 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;
|
||||
// Enforce mutex guard lock only occur inside below bracket for proper compile build.
|
||||
DSoundMutexGuardLock;
|
||||
|
||||
// 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);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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.
|
||||
|
|
|
@ -81,3 +81,6 @@ 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);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
#include <cmath>
|
||||
#include <iomanip> // For std::setfill and std::setw
|
||||
#include <filesystem>
|
||||
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
|
@ -205,6 +206,7 @@ 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)
|
||||
|
@ -438,22 +440,6 @@ 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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
670
src/core/hle/JVS/JVS.cpp
Normal file
670
src/core/hle/JVS/JVS.cpp
Normal file
|
@ -0,0 +1,670 @@
|
|||
// 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 ®ion = (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);
|
||||
}
|
182
src/core/hle/JVS/JVS.h
Normal file
182
src/core/hle/JVS/JVS.h
Normal file
|
@ -0,0 +1,182 @@
|
|||
// ******************************************************************
|
||||
// * 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
|
|
@ -29,6 +29,7 @@
|
|||
#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"
|
||||
|
@ -57,13 +58,16 @@ 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", xbox::EMUPATCH(D3DDevice_BeginPush), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_BeginPush2", xbox::EMUPATCH(D3DDevice_BeginPush2), 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_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),
|
||||
|
@ -74,9 +78,10 @@ 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", xbox::EMUPATCH(D3DDevice_DrawVertices_4), 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_DrawVerticesUP", xbox::EMUPATCH(D3DDevice_DrawVerticesUP), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_DrawVerticesUP_12", xbox::EMUPATCH(D3DDevice_DrawVerticesUP_12), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_DrawVerticesUP_12__LTCG_ebx3", xbox::EMUPATCH(D3DDevice_DrawVerticesUP_12__LTCG_ebx3), 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),
|
||||
|
@ -118,12 +123,14 @@ 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", xbox::EMUPATCH(D3DDevice_SelectVertexShader_0), PATCH_HLE_D3D),
|
||||
PATCH_ENTRY("D3DDevice_SelectVertexShader_4", xbox::EMUPATCH(D3DDevice_SelectVertexShader_4), 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_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),
|
||||
|
@ -138,7 +145,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", xbox::EMUPATCH(D3DDevice_SetPixelShader_0), 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_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),
|
||||
|
@ -156,7 +163,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", xbox::EMUPATCH(D3DDevice_SetTexture_4), 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_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),
|
||||
|
@ -177,7 +184,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),
|
||||
|
@ -369,6 +376,54 @@ 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;
|
||||
|
|
|
@ -399,91 +399,34 @@ void DestructHleInputDevice(DeviceState *dev)
|
|||
|
||||
void SetupXboxDeviceTypes()
|
||||
{
|
||||
// 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 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);
|
||||
EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_MEMORY_UNIT found at 0x%08X", reinterpret_cast<uintptr_t>(g_DeviceType_MU));
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::INFO, "XDEVICE_TYPE_MEMORY_UNIT was not found by XbSymbolDatabase");
|
||||
}
|
||||
|
||||
// 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);
|
||||
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));
|
||||
g_XapiMountedMUs = reinterpret_cast<xbox::ulong_xt*>(xapi_mounted_mu);
|
||||
}
|
||||
else {
|
||||
EmuLog(LOG_LEVEL::INFO, "XapiMountedMUs was not found by XbSymbolDatabase");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1017,15 +960,41 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait)
|
|||
NewTime.QuadPart += (static_cast<xbox::ulonglong_xt>(dwMilliseconds) * CLOCK_TIME_INCREMENT);
|
||||
}
|
||||
|
||||
xbox::dword_xt ret = WaitApc([hObjectToSignal, hObjectToWaitOn, bAlertable]() -> std::optional<DWORD> {
|
||||
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> {
|
||||
DWORD dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, 0, bAlertable);
|
||||
if (dwRet == WAIT_TIMEOUT) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional<DWORD>(dwRet);
|
||||
}, Timeout, bAlertable, UserMode);
|
||||
// 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((ret == X_STATUS_USER_APC) ? WAIT_IO_COMPLETION : (ret == X_STATUS_TIMEOUT) ? WAIT_TIMEOUT : ret);
|
||||
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);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -605,6 +605,9 @@ XBSYSAPI EXPORTNUM(352) void_xt NTAPI RtlRip
|
|||
PCHAR Message
|
||||
);
|
||||
|
||||
void_xt RtlInitSystem();
|
||||
extern RTL_CRITICAL_SECTION NtSystemTimeCritSec;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -98,6 +98,8 @@ 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
|
||||
|
@ -266,7 +268,7 @@ typedef struct _UNICODE_STRING
|
|||
{
|
||||
ushort_xt Length;
|
||||
ushort_xt MaximumLength;
|
||||
ushort_xt *Buffer;
|
||||
wchar_xt *Buffer;
|
||||
}
|
||||
UNICODE_STRING, *PUNICODE_STRING;
|
||||
|
||||
|
@ -1945,7 +1947,7 @@ typedef struct _KTHREAD
|
|||
/* 0x56/86 */ char_xt WaitNext;
|
||||
/* 0x57/87 */ char_xt WaitReason;
|
||||
/* 0x58/88 */ PKWAIT_BLOCK WaitBlockList;
|
||||
/* 0x5C/92 */ LIST_ENTRY WaitListEntry;
|
||||
/* 0x5C/92 */ LIST_ENTRY WaitListEntry; // Used to place the thread in the ready list of the scheduler
|
||||
/* 0x64/100 */ ulong_xt WaitTime;
|
||||
/* 0x68/104 */ ulong_xt KernelApcDisable;
|
||||
/* 0x6C/108 */ ulong_xt Quantum;
|
||||
|
@ -1969,6 +1971,8 @@ typedef struct _KTHREAD
|
|||
}
|
||||
KTHREAD, *PKTHREAD, *RESTRICTED_POINTER PRKTHREAD;
|
||||
|
||||
#define X_MAXIMUM_SUSPEND_COUNT 0x7F
|
||||
|
||||
// ******************************************************************
|
||||
// * ETHREAD
|
||||
// ******************************************************************
|
||||
|
@ -2081,6 +2085,11 @@ 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
|
||||
// ******************************************************************
|
||||
|
|
|
@ -85,6 +85,8 @@ 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;
|
||||
|
@ -140,8 +142,6 @@ void RestoreInterruptMode(bool value)
|
|||
g_bInterruptsEnabled = value;
|
||||
}
|
||||
|
||||
extern void ExecuteDpcQueue();
|
||||
|
||||
void KiUnexpectedInterrupt()
|
||||
{
|
||||
xbox::KeBugCheck(TRAP_CAUSE_UNKNOWN); // see
|
||||
|
@ -158,7 +158,10 @@ void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql)
|
|||
xbox::KiExecuteKernelApc();
|
||||
break;
|
||||
case DISPATCH_LEVEL: // = 2
|
||||
ExecuteDpcQueue();
|
||||
// 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();
|
||||
}
|
||||
break;
|
||||
case APC_LEVEL | DISPATCH_LEVEL: // = 3
|
||||
KiUnexpectedInterrupt();
|
||||
|
@ -175,6 +178,33 @@ 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
|
||||
|
@ -450,7 +480,8 @@ 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
|
||||
if (!(KeGetCurrentPrcb()->DpcRoutineActive)) { // Avoid KeIsExecutingDpc(), as that logs
|
||||
// Checking DpcRoutineActive doesn't work because our Prcb is per-thread instead of being per-processor
|
||||
if (!IsDpcActive()) { // Avoid KeIsExecutingDpc(), as that logs
|
||||
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
inline std::condition_variable g_InterruptSignal;
|
||||
inline std::atomic_bool g_AnyInterruptAsserted = false;
|
||||
// Indicates to disable/enable all interrupts when cli and sti instructions are executed
|
||||
inline std::atomic_bool g_bEnableAllInterrupts = true;
|
||||
|
||||
class HalSystemInterrupt {
|
||||
public:
|
||||
|
@ -64,8 +64,6 @@ public:
|
|||
}
|
||||
|
||||
m_Asserted = state;
|
||||
g_AnyInterruptAsserted = true;
|
||||
g_InterruptSignal.notify_one();
|
||||
};
|
||||
|
||||
void Enable() {
|
||||
|
@ -110,17 +108,18 @@ 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::PETHREAD eThread, xbox::boolean_xt Alertable, xbox::char_xt WaitMode)
|
||||
std::optional<xbox::ntstatus_xt> SatisfyWait(T &&Lambda, xbox::PKTHREAD kThread, xbox::boolean_xt Alertable, xbox::char_xt WaitMode)
|
||||
{
|
||||
if (const auto ret = Lambda()) {
|
||||
if (const auto ret = Lambda(kThread)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
xbox::KiApcListMtx.lock();
|
||||
bool EmptyKernel = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[xbox::KernelMode]);
|
||||
bool EmptyUser = IsListEmpty(&eThread->Tcb.ApcState.ApcListHead[xbox::UserMode]);
|
||||
bool EmptyKernel = IsListEmpty(&kThread->ApcState.ApcListHead[xbox::KernelMode]);
|
||||
bool EmptyUser = IsListEmpty(&kThread->ApcState.ApcListHead[xbox::UserMode]);
|
||||
xbox::KiApcListMtx.unlock();
|
||||
|
||||
if (EmptyKernel == false) {
|
||||
|
@ -131,56 +130,65 @@ std::optional<xbox::ntstatus_xt> SatisfyWait(T &&Lambda, xbox::PETHREAD eThread,
|
|||
(Alertable == TRUE) &&
|
||||
(WaitMode == xbox::UserMode)) {
|
||||
xbox::KiExecuteUserApc();
|
||||
return X_STATUS_USER_APC;
|
||||
xbox::KiUnwaitThreadAndLock(kThread, X_STATUS_USER_APC, 0);
|
||||
return kThread->WaitStatus;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
xbox::ntstatus_xt WaitApc(T &&Lambda, xbox::PLARGE_INTEGER Timeout, xbox::boolean_xt Alertable, xbox::char_xt WaitMode)
|
||||
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)
|
||||
{
|
||||
// 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);
|
||||
// NOTE1: kThread->Alerted is currently never set. When the alerted mechanism is implemented, the alerts should
|
||||
// also interrupt the wait.
|
||||
|
||||
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, eThread, Alertable, WaitMode)) {
|
||||
return *ret;
|
||||
if (const auto ret = SatisfyWait(Lambda, kThread, Alertable, WaitMode)) {
|
||||
status = *ret;
|
||||
break;
|
||||
}
|
||||
|
||||
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, eThread, Alertable, WaitMode)) {
|
||||
return *ret;
|
||||
if (const auto ret = SatisfyWait(Lambda, kThread, Alertable, WaitMode)) {
|
||||
status = *ret;
|
||||
}
|
||||
else {
|
||||
return X_STATUS_TIMEOUT;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// A non-zero timeout means we have to check the conditions until we reach the requested time
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
std::this_thread::yield();
|
||||
Now = xbox::KeQueryInterruptTime();
|
||||
}
|
||||
|
||||
return X_STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
if constexpr (host_wait) {
|
||||
kThread->State = xbox::Running;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
#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
|
||||
|
@ -583,6 +582,7 @@ 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();
|
||||
|
||||
|
|
|
@ -248,6 +248,14 @@ 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
|
||||
|
@ -273,6 +281,17 @@ 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
|
||||
|
|
|
@ -96,11 +96,13 @@ namespace NtDll
|
|||
// TODO : Move towards thread-simulation based Dpc emulation
|
||||
typedef struct _DpcData {
|
||||
CRITICAL_SECTION Lock;
|
||||
HANDLE DpcEvent;
|
||||
std::atomic_flag IsDpcActive;
|
||||
std::atomic_flag IsDpcPending;
|
||||
xbox::LIST_ENTRY DpcQueue; // TODO : Use KeGetCurrentPrcb()->DpcListHead instead
|
||||
} DpcData;
|
||||
|
||||
DpcData g_DpcData = { 0 }; // Note : g_DpcData is initialized in InitDpcThread()
|
||||
DpcData g_DpcData = { 0 }; // Note : g_DpcData is initialized in InitDpcData()
|
||||
std::atomic_flag xbox::KeSystemTimeChanged;
|
||||
|
||||
xbox::ulonglong_xt LARGE_INTEGER2ULONGLONG(xbox::LARGE_INTEGER value)
|
||||
{
|
||||
|
@ -130,6 +132,33 @@ 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()
|
||||
|
@ -166,7 +195,7 @@ xbox::void_xt NTAPI xbox::KeSetSystemTime
|
|||
)
|
||||
{
|
||||
KIRQL OldIrql, OldIrql2;
|
||||
LARGE_INTEGER DeltaTime, HostTime;
|
||||
LARGE_INTEGER DeltaTime;
|
||||
PLIST_ENTRY ListHead, NextEntry;
|
||||
PKTIMER Timer;
|
||||
LIST_ENTRY TempList, TempList2;
|
||||
|
@ -184,10 +213,6 @@ 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;
|
||||
|
||||
|
@ -246,7 +271,7 @@ xbox::void_xt NTAPI xbox::KeSetSystemTime
|
|||
}
|
||||
}
|
||||
|
||||
/* Process expired timers. This releases the dispatcher and timer locks */
|
||||
/* Process expired timers. This releases the dispatcher and timer locks, then it yields */
|
||||
KiTimerListExpire(&TempList2, OldIrql);
|
||||
}
|
||||
|
||||
|
@ -460,8 +485,11 @@ 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
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Global DpcQueue, calling DPC at 0x%.8X", pkdpc->DeferredRoutine);
|
||||
LeaveCriticalSection(&(g_DpcData.Lock));
|
||||
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Global DpcQueue, calling DPC object 0x%.8X at 0x%.8X", pkdpc, pkdpc->DeferredRoutine);
|
||||
|
||||
// Call the Deferred Procedure :
|
||||
pkdpc->DeferredRoutine(
|
||||
|
@ -470,23 +498,30 @@ 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 InitDpcThread()
|
||||
void InitDpcData()
|
||||
{
|
||||
DWORD dwThreadId = 0;
|
||||
|
||||
// Let's initialize the Dpc handling thread too,
|
||||
// here for now (should be called by our caller)
|
||||
InitializeCriticalSection(&(g_DpcData.Lock));
|
||||
InitializeListHead(&(g_DpcData.DpcQueue));
|
||||
EmuLogEx(CXBXR_MODULE::INIT, LOG_LEVEL::DEBUG, "Creating DPC event\n");
|
||||
g_DpcData.DpcEvent = CreateEvent(/*lpEventAttributes=*/nullptr, /*bManualReset=*/FALSE, /*bInitialState=*/FALSE, /*lpName=*/nullptr);
|
||||
}
|
||||
|
||||
bool IsDpcActive()
|
||||
{
|
||||
return g_DpcData.IsDpcActive.test();
|
||||
}
|
||||
|
||||
static constexpr uint32_t XBOX_TSC_FREQUENCY = 733333333; // Xbox Time Stamp Counter Frequency = 733333333 (CPU Clock)
|
||||
|
@ -498,13 +533,6 @@ 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()
|
||||
// ******************************************************************
|
||||
|
@ -715,7 +743,13 @@ 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
|
||||
|
||||
xbox::ntstatus_xt ret = WaitApc([Alertable]() -> std::optional<ntstatus_xt> {
|
||||
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> {
|
||||
NtDll::LARGE_INTEGER ExpireTime;
|
||||
ExpireTime.QuadPart = 0;
|
||||
NTSTATUS Status = NtDll::NtDelayExecution(Alertable, &ExpireTime);
|
||||
|
@ -723,8 +757,11 @@ XBSYSAPI EXPORTNUM(99) xbox::ntstatus_xt NTAPI xbox::KeDelayExecutionThread
|
|||
if (Status >= 0 && Status != STATUS_ALERTED && Status != STATUS_USER_APC) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional<ntstatus_xt>(Status);
|
||||
}, Interval, Alertable, WaitMode);
|
||||
// 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);
|
||||
|
||||
if (ret == X_STATUS_TIMEOUT) {
|
||||
// NOTE: this function considers a timeout a success
|
||||
|
@ -1206,35 +1243,9 @@ XBSYSAPI EXPORTNUM(118) xbox::boolean_xt NTAPI xbox::KeInsertQueueApc
|
|||
Apc->SystemArgument1 = SystemArgument1;
|
||||
Apc->SystemArgument2 = SystemArgument2;
|
||||
|
||||
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);
|
||||
}
|
||||
boolean_xt result = KiInsertQueueApc(Apc, Increment);
|
||||
KfLowerIrql(OldIrql);
|
||||
RETURN(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1266,16 +1277,25 @@ 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
|
||||
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
|
||||
if (!IsDpcActive()) {
|
||||
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);
|
||||
|
@ -1289,7 +1309,12 @@ 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);
|
||||
}
|
||||
|
@ -1346,13 +1371,14 @@ 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;
|
||||
// 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);
|
||||
KiWaitTest(Event, Increment);
|
||||
std::this_thread::yield();
|
||||
}
|
||||
else {
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
|
||||
Event->Header.SignalState = 0;
|
||||
|
@ -1378,9 +1404,7 @@ XBSYSAPI EXPORTNUM(124) xbox::long_xt NTAPI xbox::KeQueryBasePriorityThread
|
|||
KIRQL OldIrql;
|
||||
KiLockDispatcherDatabase(&OldIrql);
|
||||
|
||||
// It cannot fail because all thread handles are created by ob
|
||||
const auto& nativeHandle = GetNativeHandle<true>(PspGetCurrentThread()->UniqueThread);
|
||||
long_xt ret = GetThreadPriority(*nativeHandle);
|
||||
long_xt ret = Thread->Priority;
|
||||
|
||||
KiUnlockDispatcherDatabase(OldIrql);
|
||||
|
||||
|
@ -1533,12 +1557,14 @@ XBSYSAPI EXPORTNUM(132) xbox::long_xt NTAPI xbox::KeReleaseSemaphore
|
|||
}
|
||||
Semaphore->Header.SignalState = adjusted_signalstate;
|
||||
|
||||
//TODO: Implement KiWaitTest
|
||||
#if 0
|
||||
KiWaitListLock();
|
||||
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();
|
||||
|
@ -1752,11 +1778,29 @@ XBSYSAPI EXPORTNUM(140) xbox::ulong_xt NTAPI xbox::KeResumeThread
|
|||
{
|
||||
LOG_FUNC_ONE_ARG(Thread);
|
||||
|
||||
NTSTATUS ret = X_STATUS_SUCCESS;
|
||||
KIRQL OldIrql;
|
||||
KiLockDispatcherDatabase(&OldIrql);
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
RETURN(ret);
|
||||
KiUnlockDispatcherDatabase(OldIrql);
|
||||
|
||||
RETURN(OldCount);
|
||||
}
|
||||
|
||||
XBSYSAPI EXPORTNUM(141) xbox::PLIST_ENTRY NTAPI xbox::KeRundownQueue
|
||||
|
@ -1798,25 +1842,15 @@ XBSYSAPI EXPORTNUM(143) xbox::long_xt NTAPI xbox::KeSetBasePriorityThread
|
|||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG_OUT(Thread)
|
||||
LOG_FUNC_ARG_OUT(Priority)
|
||||
LOG_FUNC_ARG(Thread)
|
||||
LOG_FUNC_ARG(Priority)
|
||||
LOG_FUNC_END;
|
||||
|
||||
KIRQL oldIRQL;
|
||||
KiLockDispatcherDatabase(&oldIRQL);
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
Thread->Priority = Priority;
|
||||
long_xt ret = Thread->Priority;
|
||||
|
||||
KiUnlockDispatcherDatabase(oldIRQL);
|
||||
|
||||
|
@ -1837,17 +1871,9 @@ 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);
|
||||
|
@ -1882,7 +1908,9 @@ 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);
|
||||
|
@ -1890,16 +1918,14 @@ XBSYSAPI EXPORTNUM(145) xbox::long_xt NTAPI xbox::KeSetEvent
|
|||
(WaitBlock->WaitType != WaitAny)) {
|
||||
if (OldState == 0) {
|
||||
Event->Header.SignalState = 1;
|
||||
// TODO: KiWaitTest(Event, Increment);
|
||||
// For now, we just sleep to give other threads time to wake
|
||||
// See KePulseEvent
|
||||
Sleep(1);
|
||||
KiWaitTest(Event, Increment);
|
||||
}
|
||||
else {
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
} else {
|
||||
// TODO: KiUnwaitThread(WaitBlock->Thread, (NTSTATUS)WaitBlock->WaitKey, Increment);
|
||||
// For now, we just sleep to give other threads time to wake
|
||||
// See KePulseEvent
|
||||
Sleep(1);
|
||||
KiUnwaitThread(WaitBlock->Thread, (NTSTATUS)WaitBlock->WaitKey, Increment);
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1936,6 +1962,7 @@ XBSYSAPI EXPORTNUM(146) xbox::void_xt NTAPI xbox::KeSetEventBoostPriority
|
|||
return;
|
||||
}
|
||||
|
||||
KiWaitListLock();
|
||||
if (IsListEmpty(&Event->Header.WaitListHead) != FALSE) {
|
||||
Event->Header.SignalState = 1;
|
||||
} else {
|
||||
|
@ -1946,11 +1973,9 @@ XBSYSAPI EXPORTNUM(146) xbox::void_xt NTAPI xbox::KeSetEventBoostPriority
|
|||
}
|
||||
|
||||
WaitThread->Quantum = WaitThread->ApcState.Process->ThreadQuantum;
|
||||
// TODO: KiUnwaitThread(WaitThread, X_STATUS_SUCCESS, 1);
|
||||
// For now, we just sleep to give other threads time to wake
|
||||
// See KePulseEvent
|
||||
Sleep(1);
|
||||
KiUnwaitThread(WaitThread, X_STATUS_SUCCESS, 1);
|
||||
}
|
||||
KiWaitListUnlock();
|
||||
|
||||
KiUnlockDispatcherDatabase(OldIrql);
|
||||
}
|
||||
|
@ -1982,8 +2007,8 @@ XBSYSAPI EXPORTNUM(148) xbox::boolean_xt NTAPI xbox::KeSetPriorityThread
|
|||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG_OUT(Thread)
|
||||
LOG_FUNC_ARG_OUT(Priority)
|
||||
LOG_FUNC_ARG(Thread)
|
||||
LOG_FUNC_ARG(Priority)
|
||||
LOG_FUNC_END;
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
@ -2096,11 +2121,38 @@ XBSYSAPI EXPORTNUM(152) xbox::ulong_xt NTAPI xbox::KeSuspendThread
|
|||
{
|
||||
LOG_FUNC_ONE_ARG(Thread);
|
||||
|
||||
NTSTATUS ret = X_STATUS_SUCCESS;
|
||||
KIRQL OldIrql;
|
||||
KiLockDispatcherDatabase(&OldIrql);
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
char_xt OldCount = Thread->SuspendCount;
|
||||
if (OldCount == X_MAXIMUM_SUSPEND_COUNT) {
|
||||
KiUnlockDispatcherDatabase(OldIrql);
|
||||
RETURN(X_STATUS_SUSPEND_COUNT_EXCEEDED);
|
||||
}
|
||||
|
||||
RETURN(ret);
|
||||
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);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -2196,15 +2248,13 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|||
// Wait Loop
|
||||
// This loop ends
|
||||
PLARGE_INTEGER OriginalTime = Timeout;
|
||||
LARGE_INTEGER DueTime, NewTime;
|
||||
KWAIT_BLOCK StackWaitBlock;
|
||||
PKWAIT_BLOCK WaitBlock = &StackWaitBlock;
|
||||
PKWAIT_BLOCK WaitBlock;
|
||||
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);
|
||||
|
@ -2263,7 +2313,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];
|
||||
KiWaitSatisfyAll(WaitBlock);
|
||||
KiWaitSatisfyAllAndLock(WaitBlock);
|
||||
WaitStatus = (NTSTATUS)Thread->WaitStatus;
|
||||
goto NoWait;
|
||||
}
|
||||
|
@ -2278,36 +2328,20 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|||
goto NoWait;
|
||||
}
|
||||
|
||||
// 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;
|
||||
// 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;
|
||||
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;
|
||||
|
@ -2315,12 +2349,13 @@ 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
|
||||
|
@ -2328,9 +2363,6 @@ 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) {
|
||||
|
@ -2342,7 +2374,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();
|
||||
|
@ -2357,12 +2389,15 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
|
|||
//}
|
||||
|
||||
// TODO: Remove this after we have our own scheduler and the above is implemented
|
||||
Sleep(0);
|
||||
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);
|
||||
|
||||
// Reduce the timout if necessary
|
||||
if (Timeout != nullptr) {
|
||||
Timeout = KiComputeWaitInterval(OriginalTime, &DueTime, &NewTime);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Raise IRQL to DISPATCH_LEVEL and lock the database (only if it's not already at this level)
|
||||
|
@ -2377,10 +2412,14 @@ 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);
|
||||
|
||||
|
@ -2389,14 +2428,7 @@ NoWait:
|
|||
// Unlock the database and return the status
|
||||
//TODO: KiAdjustQuantumThread(Thread);
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
Thread->State = Running;
|
||||
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
||||
|
||||
if (WaitStatus == X_STATUS_USER_APC) {
|
||||
|
@ -2438,12 +2470,9 @@ 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 WaitStatus;
|
||||
// Hack variable (remove this when the thread scheduler is here)
|
||||
bool timeout_set = false;
|
||||
ntstatus_xt WaitStatus;
|
||||
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)) {
|
||||
|
@ -2491,36 +2520,20 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
|
|||
goto NoWait;
|
||||
}
|
||||
|
||||
// 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;
|
||||
// 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;
|
||||
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;
|
||||
|
@ -2532,9 +2545,6 @@ 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) {
|
||||
|
@ -2546,7 +2556,7 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
|
|||
Thread->WaitMode = WaitMode;
|
||||
Thread->WaitReason = (UCHAR)WaitReason;
|
||||
Thread->WaitTime = KeTickCount;
|
||||
// TODO: Thread->State = Waiting;
|
||||
Thread->State = Waiting;
|
||||
//KiInsertWaitList(WaitMode, Thread);
|
||||
|
||||
/*
|
||||
|
@ -2561,13 +2571,21 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
|
|||
return WaitStatus;
|
||||
} */
|
||||
|
||||
// TODO: Remove this after we have our own scheduler and the above is implemented
|
||||
Sleep(0);
|
||||
// Insert the WaitBlock
|
||||
KiWaitListLock();
|
||||
InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
|
||||
KiWaitListUnlock();
|
||||
|
||||
// Reduce the timout if necessary
|
||||
if (Timeout != nullptr) {
|
||||
Timeout = KiComputeWaitInterval(OriginalTime, &DueTime, &NewTime);
|
||||
}
|
||||
// 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);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Raise IRQL to DISPATCH_LEVEL and lock the database
|
||||
|
@ -2582,10 +2600,14 @@ 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);
|
||||
|
||||
|
@ -2594,14 +2616,7 @@ NoWait:
|
|||
// Unlock the database and return the status
|
||||
//TODO: KiAdjustQuantumThread(Thread);
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
Thread->State = Running;
|
||||
KiUnlockDispatcherDatabase(Thread->WaitIrql);
|
||||
|
||||
if (WaitStatus == X_STATUS_USER_APC) {
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
|
||||
namespace xbox
|
||||
{
|
||||
extern std::atomic_flag KeSystemTimeChanged;
|
||||
|
||||
void_xt NTAPI KeSetSystemTime
|
||||
(
|
||||
IN PLARGE_INTEGER NewTime,
|
||||
|
@ -50,5 +52,16 @@ 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();
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@
|
|||
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
||||
*/
|
||||
|
||||
// Also from ReactOS: KiWaitTest, KiWaitSatisfyAll, KiUnwaitThread, KiUnlinkThread
|
||||
|
||||
// COPYING file:
|
||||
/*
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
@ -84,15 +86,18 @@ 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;
|
||||
|
@ -129,55 +134,81 @@ xbox::void_xt xbox::KiTimerUnlock()
|
|||
KiTimerMtx.Mtx.unlock();
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::KiClockIsr
|
||||
(
|
||||
unsigned int ScalingFactor
|
||||
)
|
||||
xbox::void_xt xbox::KiWaitListLock()
|
||||
{
|
||||
KIRQL OldIrql;
|
||||
LARGE_INTEGER InterruptTime;
|
||||
LARGE_INTEGER HostSystemTime;
|
||||
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;
|
||||
ULONG Hand;
|
||||
DWORD OldKeTickCount;
|
||||
|
||||
OldIrql = KfRaiseIrql(CLOCK_LEVEL);
|
||||
static uint64_t LostUs;
|
||||
uint64_t TotalMs = TotalUs / 1000;
|
||||
LostUs += (TotalUs - TotalMs * 1000);
|
||||
uint64_t RecoveredMs = LostUs / 1000;
|
||||
TotalMs += RecoveredMs;
|
||||
LostUs -= (RecoveredMs * 1000);
|
||||
|
||||
// Update the interrupt time
|
||||
InterruptTime.u.LowPart = KeInterruptTime.LowPart;
|
||||
InterruptTime.u.HighPart = KeInterruptTime.High1Time;
|
||||
InterruptTime.QuadPart += (CLOCK_TIME_INCREMENT * ScalingFactor);
|
||||
InterruptTime.QuadPart += (CLOCK_TIME_INCREMENT * TotalMs);
|
||||
KeInterruptTime.High2Time = InterruptTime.u.HighPart;
|
||||
KeInterruptTime.LowPart = InterruptTime.u.LowPart;
|
||||
KeInterruptTime.High1Time = InterruptTime.u.HighPart;
|
||||
|
||||
// Update the system time
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
|
||||
// Update the tick counter
|
||||
OldKeTickCount = KeTickCount;
|
||||
KeTickCount += ScalingFactor;
|
||||
KeTickCount += static_cast<dword_xt>(TotalMs);
|
||||
|
||||
// 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
|
||||
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);
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
KiTimerMtx.Acquired--;
|
||||
KiTimerMtx.Mtx.unlock();
|
||||
}
|
||||
|
||||
KfLowerIrql(OldIrql);
|
||||
}
|
||||
|
||||
xbox::void_xt NTAPI xbox::KiCheckTimerTable
|
||||
|
@ -328,8 +359,8 @@ xbox::boolean_xt FASTCALL xbox::KiInsertTimerTable
|
|||
IN xbox::ulong_xt Hand
|
||||
)
|
||||
{
|
||||
LARGE_INTEGER InterruptTime;
|
||||
LONGLONG DueTime = Timer->DueTime.QuadPart;
|
||||
ULARGE_INTEGER InterruptTime;
|
||||
ULONGLONG DueTime = Timer->DueTime.QuadPart;
|
||||
BOOLEAN Expired = FALSE;
|
||||
PLIST_ENTRY ListHead, NextEntry;
|
||||
PKTIMER CurrentTimer;
|
||||
|
@ -352,7 +383,7 @@ xbox::boolean_xt FASTCALL xbox::KiInsertTimerTable
|
|||
CurrentTimer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
|
||||
|
||||
/* Now check if we can fit it before */
|
||||
if ((ULONGLONG)DueTime >= CurrentTimer->DueTime.QuadPart) break;
|
||||
if (DueTime >= CurrentTimer->DueTime.QuadPart) break;
|
||||
|
||||
/* Keep looping */
|
||||
NextEntry = NextEntry->Blink;
|
||||
|
@ -368,6 +399,10 @@ 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);
|
||||
|
@ -484,26 +519,20 @@ xbox::boolean_xt FASTCALL xbox::KiSignalTimer
|
|||
Timer->Header.SignalState = TRUE;
|
||||
|
||||
/* Check if the timer has waiters */
|
||||
KiWaitListLock();
|
||||
if (!IsListEmpty(&Timer->Header.WaitListHead))
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
KiWaitTest(Timer, 0);
|
||||
}
|
||||
else {
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
|
||||
/* Check if we have a period */
|
||||
if (Period)
|
||||
{
|
||||
/* Calculate the interval and insert the timer */
|
||||
Interval.QuadPart = Int32x32To64(Period, -10000);
|
||||
Interval.QuadPart = Period * -10000LL;
|
||||
while (!KiInsertTreeTimer(Timer, Interval));
|
||||
}
|
||||
|
||||
|
@ -532,7 +561,7 @@ xbox::void_xt NTAPI xbox::KiTimerExpiration
|
|||
{
|
||||
ULARGE_INTEGER SystemTime, InterruptTime;
|
||||
LARGE_INTEGER Interval;
|
||||
LONG Limit, Index, i;
|
||||
LONG i;
|
||||
ULONG Timers, ActiveTimers, DpcCalls;
|
||||
PLIST_ENTRY ListHead, NextEntry;
|
||||
KIRQL OldIrql;
|
||||
|
@ -544,34 +573,25 @@ 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 */
|
||||
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);
|
||||
dword_xt OldKeTickCount = PtrToLong(SystemArgument1);
|
||||
dword_xt EndKeTickCount = PtrToLong(SystemArgument2);
|
||||
|
||||
/* Setup accounting data */
|
||||
DpcCalls = 0;
|
||||
Timers = 24;
|
||||
ActiveTimers = 4;
|
||||
|
||||
/* Lock the Database and Raise IRQL */
|
||||
/* Lock the Database */
|
||||
KiTimerLock();
|
||||
KiLockDispatcherDatabase(&OldIrql);
|
||||
|
||||
/* Start expiration loop */
|
||||
do
|
||||
for (dword_xt i = OldKeTickCount; i < EndKeTickCount; ++i)
|
||||
{
|
||||
/* Get the current index */
|
||||
Index = (Index + 1) & (TIMER_TABLE_SIZE - 1);
|
||||
dword_xt Index = i & (TIMER_TABLE_SIZE - 1);
|
||||
|
||||
/* Get list pointers and loop the list */
|
||||
ListHead = &KiTimerTableListHead[Index].Entry;
|
||||
|
@ -599,26 +619,20 @@ xbox::void_xt NTAPI xbox::KiTimerExpiration
|
|||
Period = Timer->Period;
|
||||
|
||||
/* Check if there are any waiters */
|
||||
KiWaitListLock();
|
||||
if (!IsListEmpty(&Timer->Header.WaitListHead))
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
KiWaitTest(Timer, 0);
|
||||
}
|
||||
else {
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
|
||||
/* Check if we have a period */
|
||||
if (Period)
|
||||
{
|
||||
/* Calculate the interval and insert the timer */
|
||||
Interval.QuadPart = Int32x32To64(Period, -10000);
|
||||
Interval.QuadPart = Period * -10000LL;
|
||||
while (!KiInsertTreeTimer(Timer, Interval));
|
||||
}
|
||||
|
||||
|
@ -709,7 +723,7 @@ xbox::void_xt NTAPI xbox::KiTimerExpiration
|
|||
break;
|
||||
}
|
||||
}
|
||||
} while (Index != Limit);
|
||||
}
|
||||
|
||||
/* Verify the timer table, on a debug kernel only */
|
||||
if (g_bIsDebugKernel) {
|
||||
|
@ -737,17 +751,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 */
|
||||
KiUnlockDispatcherDatabase(OldIrql);
|
||||
KiTimerUnlock();
|
||||
KiUnlockDispatcherDatabase(OldIrql);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -791,26 +805,20 @@ xbox::void_xt FASTCALL xbox::KiTimerListExpire
|
|||
Period = Timer->Period;
|
||||
|
||||
/* Check if there's any waiters */
|
||||
KiWaitListLock();
|
||||
if (!IsListEmpty(&Timer->Header.WaitListHead))
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
KiWaitTest(Timer, 0);
|
||||
}
|
||||
else {
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
|
||||
/* Check if we have a period */
|
||||
if (Period)
|
||||
{
|
||||
/* Calculate the interval and insert the timer */
|
||||
Interval.QuadPart = Int32x32To64(Period, -10000);
|
||||
Interval.QuadPart = Period * -10000LL;
|
||||
while (!KiInsertTreeTimer(Timer, Interval));
|
||||
}
|
||||
|
||||
|
@ -826,6 +834,8 @@ xbox::void_xt FASTCALL xbox::KiTimerListExpire
|
|||
}
|
||||
}
|
||||
|
||||
KiTimerUnlock();
|
||||
|
||||
/* Check if we still have DPC entries */
|
||||
if (DpcCalls)
|
||||
{
|
||||
|
@ -849,39 +859,14 @@ 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()
|
||||
{
|
||||
|
@ -907,12 +892,13 @@ static xbox::void_xt KiExecuteApc()
|
|||
Apc->Inserted = FALSE;
|
||||
xbox::KiApcListMtx.unlock();
|
||||
|
||||
// NOTE: we never use KernelRoutine because that is only used for kernel APCs, which we currently don't use
|
||||
// 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);
|
||||
|
||||
if (Apc->NormalRoutine != xbox::zeroptr) {
|
||||
(Apc->NormalRoutine)(Apc->NormalContext, Apc->SystemArgument1, Apc->SystemArgument2);
|
||||
}
|
||||
|
||||
xbox::ExFreePool(Apc);
|
||||
xbox::KiApcListMtx.lock();
|
||||
}
|
||||
|
||||
|
@ -966,7 +952,8 @@ 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,
|
||||
|
@ -974,7 +961,7 @@ xbox::void_xt NTAPI xbox::KiSuspendNop(
|
|||
IN PVOID* SystemArgument2
|
||||
)
|
||||
{
|
||||
/* Does nothing */
|
||||
/* Does nothing because the memory of the suspend apc is part of kthread */
|
||||
UNREFERENCED_PARAMETER(Apc);
|
||||
UNREFERENCED_PARAMETER(NormalRoutine);
|
||||
UNREFERENCED_PARAMETER(NormalContext);
|
||||
|
@ -982,8 +969,21 @@ 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,3 +1076,210 @@ 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;
|
||||
}
|
||||
|
|
|
@ -47,6 +47,12 @@ 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;
|
||||
|
||||
|
@ -56,10 +62,11 @@ namespace xbox
|
|||
|
||||
void_xt KiTimerUnlock();
|
||||
|
||||
void_xt KiClockIsr
|
||||
(
|
||||
IN unsigned int ScalingFactor
|
||||
);
|
||||
void_xt KiWaitListLock();
|
||||
|
||||
void_xt KiWaitListUnlock();
|
||||
|
||||
void_xt KiClockIsr(ulonglong_xt TotalUs);
|
||||
|
||||
xbox::void_xt NTAPI KiCheckTimerTable
|
||||
(
|
||||
|
@ -132,7 +139,7 @@ namespace xbox
|
|||
IN KIRQL OldIrql
|
||||
);
|
||||
|
||||
void_xt FASTCALL KiWaitSatisfyAll
|
||||
void_xt KiWaitSatisfyAll
|
||||
(
|
||||
IN PKWAIT_BLOCK WaitBlock
|
||||
);
|
||||
|
@ -156,7 +163,8 @@ namespace xbox
|
|||
);
|
||||
|
||||
// Source: ReactOS
|
||||
void_xt NTAPI KiSuspendNop(
|
||||
void_xt NTAPI KiSuspendNop
|
||||
(
|
||||
IN PKAPC Apc,
|
||||
IN PKNORMAL_ROUTINE* NormalRoutine,
|
||||
IN PVOID* NormalContext,
|
||||
|
@ -164,6 +172,15 @@ 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,
|
||||
|
@ -180,6 +197,48 @@ 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;
|
||||
|
|
|
@ -47,6 +47,7 @@ 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
|
||||
|
@ -58,7 +59,7 @@ namespace NtDll
|
|||
#include <mutex>
|
||||
|
||||
// Prevent setting the system time from multiple threads at the same time
|
||||
std::mutex NtSystemTimeMtx;
|
||||
xbox::RTL_CRITICAL_SECTION xbox::NtSystemTimeCritSec;
|
||||
|
||||
// ******************************************************************
|
||||
// * 0x00B8 - NtAllocateVirtualMemory()
|
||||
|
@ -1039,7 +1040,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, zeroptr, zeroptr, reinterpret_cast<PKNORMAL_ROUTINE>(ApcRoutine), UserMode, ApcRoutineContext);
|
||||
KeInitializeApc(Apc, &Thread->Tcb, KiFreeUserApc, zeroptr, reinterpret_cast<PKNORMAL_ROUTINE>(ApcRoutine), UserMode, ApcRoutineContext);
|
||||
if (!KeInsertQueueApc(Apc, ApcStatusBlock, ApcReserved, 0)) {
|
||||
ExFreePool(Apc);
|
||||
result = X_STATUS_UNSUCCESSFUL;
|
||||
|
@ -1711,6 +1712,16 @@ 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);
|
||||
|
@ -1856,15 +1867,20 @@ XBSYSAPI EXPORTNUM(224) xbox::ntstatus_xt NTAPI xbox::NtResumeThread
|
|||
LOG_FUNC_ARG_OUT(PreviousSuspendCount)
|
||||
LOG_FUNC_END;
|
||||
|
||||
if (const auto &nativeHandle = GetNativeHandle(ThreadHandle)) {
|
||||
// Thread handles are created by ob
|
||||
RETURN(NtDll::NtResumeThread(*nativeHandle, (::PULONG)PreviousSuspendCount));
|
||||
}
|
||||
else {
|
||||
RETURN(X_STATUS_INVALID_HANDLE);
|
||||
PETHREAD Thread;
|
||||
ntstatus_xt result = ObReferenceObjectByHandle(ThreadHandle, &PsThreadObjectType, reinterpret_cast<PVOID *>(&Thread));
|
||||
if (!X_NT_SUCCESS(result)) {
|
||||
RETURN(result);
|
||||
}
|
||||
|
||||
// TODO : Once we do our own thread-switching, implement NtResumeThread using KetResumeThread
|
||||
ulong_xt PrevSuspendCount = KeResumeThread(&Thread->Tcb);
|
||||
ObfDereferenceObject(Thread);
|
||||
|
||||
if (PreviousSuspendCount) {
|
||||
*PreviousSuspendCount = PrevSuspendCount;
|
||||
}
|
||||
|
||||
RETURN(X_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -1971,7 +1987,7 @@ XBSYSAPI EXPORTNUM(228) xbox::ntstatus_xt NTAPI xbox::NtSetSystemTime
|
|||
ret = STATUS_ACCESS_VIOLATION;
|
||||
}
|
||||
else {
|
||||
NtSystemTimeMtx.lock();
|
||||
RtlEnterCriticalSectionAndRegion(&NtSystemTimeCritSec);
|
||||
NewSystemTime = *SystemTime;
|
||||
if (NewSystemTime.u.HighPart > 0 && NewSystemTime.u.HighPart <= 0x20000000) {
|
||||
/* Convert the time and set it in HAL */
|
||||
|
@ -1991,7 +2007,7 @@ XBSYSAPI EXPORTNUM(228) xbox::ntstatus_xt NTAPI xbox::NtSetSystemTime
|
|||
else {
|
||||
ret = STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
NtSystemTimeMtx.unlock();
|
||||
RtlLeaveCriticalSectionAndRegion(&NtSystemTimeCritSec);
|
||||
}
|
||||
|
||||
RETURN(ret);
|
||||
|
@ -2079,15 +2095,30 @@ XBSYSAPI EXPORTNUM(231) xbox::ntstatus_xt NTAPI xbox::NtSuspendThread
|
|||
LOG_FUNC_ARG_OUT(PreviousSuspendCount)
|
||||
LOG_FUNC_END;
|
||||
|
||||
if (const auto &nativeHandle = GetNativeHandle(ThreadHandle)) {
|
||||
// Thread handles are created by ob
|
||||
RETURN(NtDll::NtSuspendThread(*nativeHandle, (::PULONG)PreviousSuspendCount));
|
||||
}
|
||||
else {
|
||||
RETURN(X_STATUS_INVALID_HANDLE);
|
||||
PETHREAD Thread;
|
||||
ntstatus_xt result = ObReferenceObjectByHandle(ThreadHandle, &PsThreadObjectType, reinterpret_cast<PVOID *>(&Thread));
|
||||
if (!X_NT_SUCCESS(result)) {
|
||||
RETURN(result);
|
||||
}
|
||||
|
||||
// TODO : Once we do our own thread-switching, implement NtSuspendThread using KeSuspendThread
|
||||
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);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -2201,15 +2232,23 @@ 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
|
||||
|
||||
xbox::ntstatus_xt ret = WaitApc([Count, &nativeHandles, WaitType, Alertable]() -> std::optional<ntstatus_xt> {
|
||||
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> {
|
||||
NtDll::LARGE_INTEGER ExpireTime;
|
||||
ExpireTime.QuadPart = 0;
|
||||
NTSTATUS Status = NtDll::NtWaitForMultipleObjects(
|
||||
|
@ -2221,8 +2260,11 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx
|
|||
if (Status == STATUS_TIMEOUT) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::make_optional<ntstatus_xt>(Status);
|
||||
}, Timeout, Alertable, WaitMode);
|
||||
// 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(ret);
|
||||
}
|
||||
|
@ -2269,6 +2311,16 @@ 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);
|
||||
|
|
|
@ -1023,42 +1023,53 @@ 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()) {
|
||||
if ((ObjectType == &PsThreadObjectType) || (ObjectType == NULL)) {
|
||||
// We only accept either thread or null object type.
|
||||
if ((ObjectType == &PsThreadObjectType) || (!ObjectType)) {
|
||||
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);
|
||||
|
||||
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;
|
||||
// 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.
|
||||
}
|
||||
} 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)
|
||||
DWORD flags = 0;
|
||||
if (GetHandleInformation(Handle, &flags)) {
|
||||
else 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*ReturnedObject = NULL;
|
||||
|
|
|
@ -121,6 +121,8 @@ 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
|
||||
|
@ -406,13 +408,24 @@ XBSYSAPI EXPORTNUM(255) xbox::ntstatus_xt NTAPI xbox::PsCreateSystemThreadEx
|
|||
assert(dupHandle);
|
||||
RegisterXboxHandle(eThread->UniqueThread, dupHandle);
|
||||
|
||||
eThread->Tcb.Priority = GetThreadPriority(handle);
|
||||
g_AffinityPolicy->SetAffinityXbox(handle);
|
||||
|
||||
// Now that ThreadId is populated and affinity is changed, resume the thread (unless the guest passed CREATE_SUSPENDED)
|
||||
if (!CreateSuspended) {
|
||||
ResumeThread(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), 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);
|
||||
|
@ -493,10 +506,13 @@ 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)) {
|
||||
// TODO: Implement KiWaitTest's relative objects usage
|
||||
//KiWaitTest()
|
||||
assert(0);
|
||||
KiWaitTest((PVOID)&eThread->Tcb, 0);
|
||||
std::this_thread::yield();
|
||||
}
|
||||
else {
|
||||
KiWaitListUnlock();
|
||||
}
|
||||
|
||||
if (GetNativeHandle(eThread->UniqueThread)) {
|
||||
|
|
|
@ -89,6 +89,11 @@ xbox::boolean_xt RtlpCaptureStackLimits(
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
xbox::void_xt xbox::RtlInitSystem()
|
||||
{
|
||||
xbox::RtlInitializeCriticalSection(&NtSystemTimeCritSec);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
// * 0x0104 - RtlAnsiStringToUnicodeString()
|
||||
// ******************************************************************
|
||||
|
@ -108,23 +113,28 @@ 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 = (USHORT*)ExAllocatePoolWithTag(total, 'grtS'))) {
|
||||
return X_STATUS_NO_MEMORY;
|
||||
if (!(DestinationString->Buffer = (wchar_xt*)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);
|
||||
|
@ -521,23 +531,31 @@ XBSYSAPI EXPORTNUM(270) xbox::long_xt NTAPI xbox::RtlCompareString
|
|||
LOG_FUNC_ARG(CaseInSensitive)
|
||||
LOG_FUNC_END;
|
||||
|
||||
LONG result;
|
||||
const USHORT l1 = String1->Length;
|
||||
const USHORT l2 = String2->Length;
|
||||
const USHORT maxLen = (l1 <= l2 ? l1 : l2);
|
||||
|
||||
USHORT l1 = String1->Length;
|
||||
USHORT l2 = String2->Length;
|
||||
USHORT maxLen = l1 <= l2 ? l1 : l2;
|
||||
|
||||
CHAR *str1 = String1->Buffer;
|
||||
CHAR *str2 = String2->Buffer;
|
||||
const PCHAR str1 = String1->Buffer;
|
||||
const PCHAR str2 = String2->Buffer;
|
||||
|
||||
if (CaseInSensitive) {
|
||||
result = _strnicmp(str1, str2, maxLen);
|
||||
for (unsigned i = 0; i < maxLen; i++) {
|
||||
UCHAR char1 = RtlLowerChar(str1[i]);
|
||||
UCHAR char2 = RtlLowerChar(str2[i]);
|
||||
if (char1 != char2) {
|
||||
RETURN(char1 - char2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = strncmp(str1, str2, maxLen);
|
||||
for (unsigned i = 0; i < maxLen; i++) {
|
||||
if (str1[i] != str2[i]) {
|
||||
RETURN(str1[i] - str2[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RETURN(result);
|
||||
RETURN(l1 - l2);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -556,23 +574,32 @@ XBSYSAPI EXPORTNUM(271) xbox::long_xt NTAPI xbox::RtlCompareUnicodeString
|
|||
LOG_FUNC_ARG(CaseInSensitive)
|
||||
LOG_FUNC_END;
|
||||
|
||||
LONG result;
|
||||
const USHORT l1 = String1->Length;
|
||||
const USHORT l2 = String2->Length;
|
||||
const USHORT maxLen = (l1 <= l2 ? l1 : l2) / sizeof(WCHAR);
|
||||
|
||||
USHORT l1 = String1->Length;
|
||||
USHORT l2 = String2->Length;
|
||||
USHORT maxLen = l1 <= l2 ? l1 : l2;
|
||||
|
||||
WCHAR *str1 = (WCHAR*)(String1->Buffer);
|
||||
WCHAR *str2 = (WCHAR*)(String2->Buffer);
|
||||
const wchar_xt* str1 = String1->Buffer;
|
||||
const wchar_xt* str2 = String2->Buffer;
|
||||
|
||||
if (CaseInSensitive) {
|
||||
result = _wcsnicmp(str1, str2, maxLen);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = wcsncmp(str1, str2, maxLen);
|
||||
for (unsigned i = 0; i < maxLen; i++) {
|
||||
if (str1[i] != str2[i]) {
|
||||
RETURN(str1[i] - str2[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RETURN(result);
|
||||
RETURN(l1 - l2);
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -652,7 +679,7 @@ XBSYSAPI EXPORTNUM(274) xbox::boolean_xt NTAPI xbox::RtlCreateUnicodeString
|
|||
BOOLEAN result = TRUE;
|
||||
|
||||
ULONG bufferSize = (std::u16string(SourceString).length() + 1) * sizeof(WCHAR);
|
||||
DestinationString->Buffer = (USHORT *)ExAllocatePoolWithTag(bufferSize, 'grtS');
|
||||
DestinationString->Buffer = (wchar_xt*)ExAllocatePoolWithTag(bufferSize, 'grtS');
|
||||
if (!DestinationString->Buffer) {
|
||||
result = FALSE;
|
||||
}
|
||||
|
@ -700,7 +727,7 @@ XBSYSAPI EXPORTNUM(276) xbox::ntstatus_xt NTAPI xbox::RtlDowncaseUnicodeString
|
|||
|
||||
if (AllocateDestinationString) {
|
||||
DestinationString->MaximumLength = SourceString->Length;
|
||||
DestinationString->Buffer = (USHORT*)ExAllocatePoolWithTag((ULONG)DestinationString->MaximumLength, 'grtS');
|
||||
DestinationString->Buffer = (wchar_xt*)ExAllocatePoolWithTag((ULONG)DestinationString->MaximumLength, 'grtS');
|
||||
if (DestinationString->Buffer == NULL) {
|
||||
return X_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
@ -1173,9 +1200,8 @@ XBSYSAPI EXPORTNUM(290) xbox::void_xt NTAPI xbox::RtlInitUnicodeString
|
|||
LOG_FUNC_ARG(SourceString)
|
||||
LOG_FUNC_END;
|
||||
|
||||
DestinationString->Buffer = (USHORT*)SourceString;
|
||||
DestinationString->Buffer = (wchar_xt*)SourceString;
|
||||
if (SourceString != NULL) {
|
||||
DestinationString->Buffer = (USHORT*)SourceString;
|
||||
DestinationString->Length = (USHORT)std::u16string(SourceString).length() * 2;
|
||||
DestinationString->MaximumLength = DestinationString->Length + 2;
|
||||
}
|
||||
|
@ -1523,6 +1549,7 @@ no_mapping:
|
|||
|
||||
#define TICKSPERSEC 10000000
|
||||
#define TICKSPERMSEC 10000
|
||||
#define MSECSPERSEC 1000
|
||||
#define SECSPERDAY 86400
|
||||
#define SECSPERHOUR 3600
|
||||
#define SECSPERMIN 60
|
||||
|
@ -1541,6 +1568,11 @@ 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] =
|
||||
{
|
||||
|
@ -1594,26 +1626,31 @@ 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;
|
||||
|
||||
/* 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)
|
||||
/* Verify each TimeFields' variables are within range */
|
||||
if (TimeFields->Millisecond < 0 || TimeFields->Millisecond > 999) {
|
||||
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
|
||||
|
@ -1629,20 +1666,21 @@ 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 * dayperyr, 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 * DayPerYear, corrected */
|
||||
(1959 * month) / 64 + /* months * DayPerMonth */
|
||||
TimeFields->Day - /* day of the month */
|
||||
584817; /* zero that on 1601-01-01 */
|
||||
/* done */
|
||||
|
||||
Time->QuadPart = (((((LONGLONG)day * HOURSPERDAY +
|
||||
TimeFields->Hour) * MINSPERHOUR +
|
||||
TimeFields->Minute) * SECSPERMIN +
|
||||
TimeFields->Second) * 1000 +
|
||||
TimeFields->Millisecond) * TICKSPERMSEC;
|
||||
/* 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;
|
||||
|
||||
RETURN(TRUE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ******************************************************************
|
||||
|
@ -1654,36 +1692,27 @@ XBSYSAPI EXPORTNUM(305) xbox::void_xt NTAPI xbox::RtlTimeToTimeFields
|
|||
OUT PTIME_FIELDS TimeFields
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Time)
|
||||
LOG_FUNC_ARG_OUT(TimeFields)
|
||||
LOG_FUNC_END;
|
||||
LONGLONG Days, cleaps, years, yearday, months;
|
||||
|
||||
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;
|
||||
/* 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;
|
||||
|
||||
/* compute time of day */
|
||||
TimeFields->Hour = (cshort_xt)(SecondsInDay / SECSPERHOUR);
|
||||
SecondsInDay = SecondsInDay % SECSPERHOUR;
|
||||
TimeFields->Minute = (cshort_xt)(SecondsInDay / SECSPERMIN);
|
||||
TimeFields->Second = (cshort_xt)(SecondsInDay % SECSPERMIN);
|
||||
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.
|
||||
|
||||
/* compute day of week */
|
||||
TimeFields->Weekday = (cshort_xt)((EPOCHWEEKDAY + Days) % DAYSPERWEEK);
|
||||
TimeFields->Weekday = (EPOCHWEEKDAY + Days) % DAYSPERWEEK;
|
||||
|
||||
/* compute year, month and day of month. */
|
||||
cleaps = (3 * ((4 * Days + 1227) / DAYSPERQUADRICENTENNIUM) + 3) / 4;
|
||||
|
@ -1692,7 +1721,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 Januari and Februari and
|
||||
* To convert take 12 from January and February and
|
||||
* increase the year by one. */
|
||||
if (months < 14) {
|
||||
TimeFields->Month = (USHORT)(months - 1);
|
||||
|
@ -2025,7 +2054,7 @@ XBSYSAPI EXPORTNUM(314) xbox::ntstatus_xt NTAPI xbox::RtlUpcaseUnicodeString
|
|||
|
||||
if (AllocateDestinationString) {
|
||||
DestinationString->MaximumLength = SourceString->Length;
|
||||
DestinationString->Buffer = (USHORT*)ExAllocatePoolWithTag((ULONG)DestinationString->MaximumLength, 'grtS');
|
||||
DestinationString->Buffer = (wchar_xt*)ExAllocatePoolWithTag((ULONG)DestinationString->MaximumLength, 'grtS');
|
||||
if (DestinationString->Buffer == NULL) {
|
||||
return X_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
@ -2219,6 +2248,11 @@ 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 */
|
||||
|
|
|
@ -41,13 +41,19 @@ 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 =
|
||||
{
|
||||
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
|
||||
// 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
|
||||
};
|
||||
|
||||
// ******************************************************************
|
||||
|
|
|
@ -99,9 +99,6 @@ 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;
|
||||
|
||||
|
@ -113,6 +110,8 @@ 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
|
||||
|
@ -329,64 +328,6 @@ 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();
|
||||
|
@ -542,7 +483,10 @@ static void CxbxrKrnlSetupMemorySystem(int BootFlags, unsigned emulate_system, u
|
|||
}
|
||||
}
|
||||
|
||||
static bool CxbxrKrnlXbeSystemSelector(int BootFlags, unsigned& reserved_systems, blocks_reserved_t blocks_reserved)
|
||||
static bool CxbxrKrnlXbeSystemSelector(int BootFlags,
|
||||
unsigned& reserved_systems,
|
||||
blocks_reserved_t blocks_reserved,
|
||||
HardwareModel &hardwareModel)
|
||||
{
|
||||
unsigned int emulate_system = 0;
|
||||
// Get title path :
|
||||
|
@ -683,6 +627,7 @@ static bool CxbxrKrnlXbeSystemSelector(int BootFlags, unsigned& reserved_systems
|
|||
// 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:
|
||||
|
@ -696,6 +641,10 @@ static bool CxbxrKrnlXbeSystemSelector(int BootFlags, unsigned& reserved_systems
|
|||
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);
|
||||
|
@ -717,6 +666,17 @@ static bool CxbxrKrnlXbeSystemSelector(int BootFlags, unsigned& reserved_systems
|
|||
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
|
||||
|
@ -934,7 +894,8 @@ static bool CxbxrKrnlPrepareXbeMap()
|
|||
Xbe::Header* XbeHeader,
|
||||
uint32_t XbeHeaderSize,
|
||||
void (*Entry)(),
|
||||
int BootFlags
|
||||
int BootFlags,
|
||||
HardwareModel hardwareModel
|
||||
);
|
||||
|
||||
void CxbxKrnlEmulate(unsigned int reserved_systems, blocks_reserved_t blocks_reserved)
|
||||
|
@ -1088,7 +1049,8 @@ void CxbxKrnlEmulate(unsigned int reserved_systems, blocks_reserved_t blocks_res
|
|||
// using XeLoadImage from LaunchDataPage->Header.szLaunchPath
|
||||
|
||||
// Now we can load the XBE :
|
||||
if (!CxbxrKrnlXbeSystemSelector(BootFlags, reserved_systems, blocks_reserved)) {
|
||||
HardwareModel hardwareModel = HardwareModel::Revision1_6; // It is configured by CxbxrKrnlXbeSystemSelector function according to console type.
|
||||
if (!CxbxrKrnlXbeSystemSelector(BootFlags, reserved_systems, blocks_reserved, hardwareModel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1116,7 +1078,8 @@ 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
|
||||
BootFlags,
|
||||
hardwareModel
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1141,7 +1104,10 @@ 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());
|
||||
EmuLogInit(LOG_LEVEL::INFO, "XBE Version : 1.%02d", g_pCertificate->dwVersion);
|
||||
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 TitleName : %.40ls", g_pCertificate->wsTitleName);
|
||||
EmuLogInit(LOG_LEVEL::INFO, "XBE Region : %s", CxbxKrnl_Xbe->GameRegionToString());
|
||||
}
|
||||
|
@ -1175,7 +1141,8 @@ static void CxbxrKrnlInitHacks()
|
|||
Xbe::Header *pXbeHeader,
|
||||
uint32_t dwXbeHeaderSize,
|
||||
void(*Entry)(),
|
||||
int BootFlags)
|
||||
int BootFlags,
|
||||
HardwareModel hardwareModel)
|
||||
{
|
||||
unsigned Host2XbStackBaseReserved = 0;
|
||||
__asm mov Host2XbStackBaseReserved, esp;
|
||||
|
@ -1199,11 +1166,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 time-related variables for the kernel and the timers
|
||||
CxbxInitPerformanceCounters();
|
||||
// Initialize DPC global
|
||||
InitDpcData();
|
||||
#ifdef _DEBUG
|
||||
// PopupCustom(LOG_LEVEL::INFO, "Attach a Debugger");
|
||||
// Debug child processes using https://marketplace.visualstudio.com/items?itemName=GreggMiskelly.MicrosoftChildProcessDebuggingPowerTool
|
||||
|
@ -1341,10 +1308,11 @@ static void CxbxrKrnlInitHacks()
|
|||
}
|
||||
xbox::PsInitSystem();
|
||||
xbox::KiInitSystem();
|
||||
|
||||
xbox::RtlInitSystem();
|
||||
|
||||
// initialize graphics
|
||||
EmuLogInit(LOG_LEVEL::DEBUG, "Initializing render window.");
|
||||
CxbxInitWindow(true);
|
||||
CxbxInitWindow();
|
||||
|
||||
// Now process the boot flags to see if there are any special conditions to handle
|
||||
if (BootFlags & BOOT_EJECT_PENDING) {} // TODO
|
||||
|
@ -1380,7 +1348,7 @@ static void CxbxrKrnlInitHacks()
|
|||
SetupXboxDeviceTypes();
|
||||
}
|
||||
|
||||
InitXboxHardware(HardwareModel::Revision1_5); // TODO : Make configurable
|
||||
InitXboxHardware(hardwareModel);
|
||||
|
||||
// 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);
|
||||
|
@ -1406,6 +1374,29 @@ 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) {
|
||||
|
@ -1430,23 +1421,17 @@ static void CxbxrKrnlInitHacks()
|
|||
#endif
|
||||
|
||||
EmuX86_Init();
|
||||
// Create the interrupt processing thread
|
||||
// Start the event thread
|
||||
xbox::HANDLE hThread;
|
||||
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, system_events, xbox::zeroptr, FALSE);
|
||||
// Launch the xbe
|
||||
xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, CxbxLaunchXbe, Entry, FALSE);
|
||||
|
||||
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; });
|
||||
xbox::KeRaiseIrqlToDpcLevel();
|
||||
while (true) {
|
||||
xbox::KeWaitForDpc();
|
||||
ExecuteDpcQueue();
|
||||
}
|
||||
}
|
||||
|
||||
// REMARK: the following is useless, but PatrickvL has asked to keep it for documentation purposes
|
||||
|
@ -1468,7 +1453,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;
|
||||
|
|
|
@ -155,7 +155,9 @@ void CxbxKrnlPanic();
|
|||
/*! empty function */
|
||||
void CxbxKrnlNoFunc();
|
||||
|
||||
void CxbxInitPerformanceCounters(); // Implemented in EmuKrnlKe.cpp
|
||||
void InitDpcData(); // Implemented in EmuKrnlKe.cpp
|
||||
bool IsDpcActive();
|
||||
void ExecuteDpcQueue();
|
||||
|
||||
/*! kernel thunk table */
|
||||
extern uint32_t CxbxKrnl_KernelThunkTable[379];
|
||||
|
|
|
@ -57,15 +57,8 @@ 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
|
||||
;
|
||||
// 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
|
||||
;
|
||||
| XBEIMAGE_MEDIA_TYPE_DVD_9_RW;
|
||||
|
||||
// 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,
|
||||
|
|
|
@ -49,11 +49,6 @@ 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);
|
||||
|
||||
|
|
|
@ -68,9 +68,6 @@ 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;
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
#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"
|
||||
|
@ -790,20 +789,21 @@ 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");
|
||||
|
||||
// Convert Wide String as returned by above to a string, for XbePath
|
||||
std::string XbePath = utf16_to_ascii(wXbePath.c_str());
|
||||
std::filesystem::path XbePath;
|
||||
|
||||
// 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);
|
||||
std::string directoryPath = utf16_to_ascii(directoryPathBuffer);
|
||||
XbePath = directoryPath + std::string("\\") + XbePath;
|
||||
XbePath = directoryPathBuffer;
|
||||
XbePath /= wXbePath;
|
||||
}
|
||||
else {
|
||||
XbePath = wXbePath;
|
||||
}
|
||||
|
||||
return XbePath;
|
||||
return XbePath.string();
|
||||
}
|
||||
|
||||
int CxbxRegisterDeviceHostPath(const std::string_view XboxDevicePath, std::string HostDevicePath, bool IsFile, std::size_t size)
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
|
||||
#include <core\kernel\exports\xboxkrnl.h>
|
||||
#include <filesystem>
|
||||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
|
|
|
@ -91,10 +91,13 @@ 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::P2L: buffer[1] = "P05"[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::D01: buffer[1] = "DXB"[m_PICVersionStringIndex]; break;
|
||||
case SCMRevision::D05: buffer[1] = "D05"[m_PICVersionStringIndex]; break; // ??
|
||||
// default: UNREACHABLE(m_revision);
|
||||
case SCMRevision::B11: buffer[1] = "B11"[m_PICVersionStringIndex]; break; // ??
|
||||
default: CxbxrAbort("Unknown PIC revision: %d", m_revision);
|
||||
}
|
||||
|
||||
m_PICVersionStringIndex = (m_PICVersionStringIndex + 1) % 3;
|
||||
|
|
|
@ -93,10 +93,14 @@
|
|||
|
||||
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 {
|
||||
|
|
|
@ -25,9 +25,14 @@
|
|||
// *
|
||||
// ******************************************************************
|
||||
|
||||
#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;
|
||||
|
@ -38,69 +43,86 @@ NVNetDevice* g_NVNet;
|
|||
NV2ADevice* g_NV2A;
|
||||
ADM1032Device* g_ADM1032;
|
||||
USBDevice* g_USB0;
|
||||
MediaBoard* g_MediaBoard;
|
||||
|
||||
MCPXRevision MCPXRevisionFromHardwareModel(HardwareModel hardwareModel)
|
||||
{
|
||||
switch (hardwareModel) {
|
||||
case Revision1_0:
|
||||
case Revision1_1:
|
||||
case Revision1_2:
|
||||
case Revision1_3:
|
||||
case Revision1_4:
|
||||
case Revision1_5:
|
||||
case Revision1_6:
|
||||
// https://xboxdevwiki.net/Xboxen_Info
|
||||
switch (GET_HW_CONSOLE(hardwareModel)) {
|
||||
case Retail:
|
||||
return MCPXRevision::MCPX_X3;
|
||||
case DebugKit:
|
||||
// EmuLog(LOG_LEVEL::WARNING, "Guessing MCPXVersion");
|
||||
case Chihiro:
|
||||
return MCPXRevision::MCPX_X2;
|
||||
default:
|
||||
// UNREACHABLE(hardwareModel);
|
||||
return MCPXRevision::MCPX_X3;
|
||||
CxbxrAbort("MCPXRevisionFromHardwareModel: Unknown conversion for hardware model (0x%02X)", hardwareModel);
|
||||
}
|
||||
}
|
||||
|
||||
SCMRevision SCMRevisionFromHardwareModel(HardwareModel hardwareModel)
|
||||
{
|
||||
// https://xboxdevwiki.net/Xboxen_Info
|
||||
switch (hardwareModel) {
|
||||
case Revision1_0:
|
||||
return SCMRevision::P01; // Our SCM returns PIC version string "P01"
|
||||
return SCMRevision::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:
|
||||
// EmuLog(LOG_LEVEL::WARNING, "Guessing SCMRevision");
|
||||
return SCMRevision::P2L; // Assumption; Our SCM returns PIC version string "P05"
|
||||
case DebugKit:
|
||||
return SCMRevision::D01; // Our SCM returns PIC version string "DXB"
|
||||
default:
|
||||
// UNREACHABLE(hardwareModel);
|
||||
return SCMRevision::P2L;
|
||||
case DebugKit:
|
||||
return SCMRevision::D01;
|
||||
case Chihiro_Type1:
|
||||
return SCMRevision::D05;
|
||||
case DebugKit_r1_2:
|
||||
case Chihiro_Type3:
|
||||
return SCMRevision::B11;
|
||||
default:
|
||||
CxbxrAbort("SCMRevisionFromHardwareModel: Unknown conversion for hardware model (0x%02X)", hardwareModel);
|
||||
}
|
||||
}
|
||||
|
||||
TVEncoder TVEncoderFromHardwareModel(HardwareModel hardwareModel)
|
||||
{
|
||||
switch (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)) {
|
||||
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;
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,22 +133,43 @@ 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);
|
||||
|
||||
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
|
||||
|
||||
// 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_EEPROM = new EEPROMDevice();
|
||||
g_NVNet = new NVNetDevice();
|
||||
g_NV2A = new NV2ADevice();
|
||||
g_ADM1032 = new ADM1032Device();
|
||||
if (bLLE_USB) {
|
||||
g_USB0 = new USBDevice();
|
||||
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);
|
||||
}
|
||||
|
||||
// Connect devices to SM bus
|
||||
|
@ -156,14 +199,12 @@ 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);
|
||||
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.
|
||||
// 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
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#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
|
||||
|
@ -53,9 +54,21 @@ typedef enum {
|
|||
Revision1_4,
|
||||
Revision1_5,
|
||||
Revision1_6,
|
||||
DebugKit
|
||||
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.
|
||||
} 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,
|
||||
|
@ -71,5 +84,6 @@ 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);
|
||||
|
|
443
src/devices/chihiro/JvsIo.cpp
Normal file
443
src/devices/chihiro/JvsIo.cpp
Normal file
|
@ -0,0 +1,443 @@
|
|||
// 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;
|
||||
}
|
215
src/devices/chihiro/JvsIo.h
Normal file
215
src/devices/chihiro/JvsIo.h
Normal file
|
@ -0,0 +1,215 @@
|
|||
// ******************************************************************
|
||||
// * 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
|
161
src/devices/chihiro/MediaBoard.cpp
Normal file
161
src/devices/chihiro/MediaBoard.cpp
Normal file
|
@ -0,0 +1,161 @@
|
|||
// 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);
|
||||
}
|
98
src/devices/chihiro/MediaBoard.h
Normal file
98
src/devices/chihiro/MediaBoard.h
Normal file
|
@ -0,0 +1,98 @@
|
|||
// ******************************************************************
|
||||
// * 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
|
|
@ -476,15 +476,18 @@ void EmuNVNet_Write(xbox::addr_xt addr, uint32_t value, int size)
|
|||
}
|
||||
|
||||
std::thread NVNetRecvThread;
|
||||
static void NVNetRecvThreadProc(NvNetState_t *s)
|
||||
void NVNetRecvThreadProc()
|
||||
{
|
||||
// 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();
|
||||
uint8_t packet[65536];
|
||||
static std::unique_ptr<uint8_t[]> packet(new uint8_t[65536]);
|
||||
while (true) {
|
||||
int size = g_NVNet->PCAPReceive(packet, 65536);
|
||||
int size = g_NVNet->PCAPReceive(packet.get(), 65536);
|
||||
if (size > 0) {
|
||||
EmuNVNet_DMAPacketToGuest(packet, size);
|
||||
}
|
||||
EmuNVNet_DMAPacketToGuest(packet.get(), size);
|
||||
}
|
||||
_mm_pause();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -527,7 +530,7 @@ void NVNetDevice::Init()
|
|||
};
|
||||
|
||||
PCAPInit();
|
||||
NVNetRecvThread = std::thread(NVNetRecvThreadProc, &NvNetState);
|
||||
NVNetRecvThread = std::thread(NVNetRecvThreadProc);
|
||||
}
|
||||
|
||||
void NVNetDevice::Reset()
|
||||
|
|
|
@ -279,11 +279,6 @@ 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;
|
||||
|
@ -358,7 +353,7 @@ void OHCI::OHCI_FrameBoundaryWorker()
|
|||
}
|
||||
|
||||
// Do SOF stuff here
|
||||
OHCI_SOF(false);
|
||||
OHCI_SOF();
|
||||
|
||||
// Writeback HCCA
|
||||
if (OHCI_WriteHCCA(m_Registers.HcHCCA, &hcca)) {
|
||||
|
@ -877,32 +872,23 @@ void OHCI::OHCI_StateReset()
|
|||
void OHCI::OHCI_BusStart()
|
||||
{
|
||||
// Create the EOF timer.
|
||||
m_pEOFtimer = Timer_Create(OHCI_FrameBoundaryWrapper, this, "", false);
|
||||
m_pEOFtimer = true;
|
||||
|
||||
EmuLog(LOG_LEVEL::DEBUG, "Operational event");
|
||||
|
||||
// SOF event
|
||||
OHCI_SOF(true);
|
||||
OHCI_SOF();
|
||||
}
|
||||
|
||||
void OHCI::OHCI_BusStop()
|
||||
{
|
||||
if (m_pEOFtimer) {
|
||||
// Delete existing EOF timer
|
||||
Timer_Exit(m_pEOFtimer);
|
||||
}
|
||||
m_pEOFtimer = nullptr;
|
||||
m_pEOFtimer = false;
|
||||
}
|
||||
|
||||
void OHCI::OHCI_SOF(bool bCreate)
|
||||
void OHCI::OHCI_SOF()
|
||||
{
|
||||
// set current SOF time
|
||||
m_SOFtime = GetTime_NS(m_pEOFtimer);
|
||||
|
||||
// make timer expire at SOF + 1 ms from now
|
||||
if (bCreate) {
|
||||
Timer_Start(m_pEOFtimer, m_UsbFrameTime);
|
||||
}
|
||||
m_SOFtime = get_now();
|
||||
|
||||
OHCI_SetInterrupt(OHCI_INTR_SF);
|
||||
}
|
||||
|
@ -1254,6 +1240,23 @@ 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)) {
|
||||
|
@ -1278,7 +1281,7 @@ uint32_t OHCI::OHCI_GetFrameRemaining()
|
|||
}
|
||||
|
||||
// Being in USB operational state guarantees that m_pEOFtimer and m_SOFtime were set already
|
||||
ticks = GetTime_NS(m_pEOFtimer) - m_SOFtime;
|
||||
ticks = get_now() - m_SOFtime;
|
||||
|
||||
// Avoid Muldiv64 if possible
|
||||
if (ticks >= m_UsbFrameTime) {
|
||||
|
|
|
@ -145,6 +145,10 @@ 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:
|
||||
|
@ -153,7 +157,7 @@ class OHCI
|
|||
// all the registers available in the OHCI standard
|
||||
OHCI_Registers m_Registers;
|
||||
// end-of-frame timer
|
||||
TimerObject* m_pEOFtimer = nullptr;
|
||||
bool m_pEOFtimer = false;
|
||||
// time at which a SOF was sent
|
||||
uint64_t m_SOFtime;
|
||||
// the duration of a usb frame
|
||||
|
@ -173,10 +177,6 @@ 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, and start a timer for EOF
|
||||
void OHCI_SOF(bool bCreate);
|
||||
// generate a SOF event
|
||||
void OHCI_SOF();
|
||||
// change interrupt status
|
||||
void OHCI_UpdateInterrupt();
|
||||
// fire an interrupt
|
||||
|
|
|
@ -82,6 +82,7 @@ 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;
|
||||
|
|
|
@ -35,17 +35,13 @@
|
|||
|
||||
#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)
|
||||
{
|
||||
// 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,
|
||||
return Muldiv64(Muldiv64(get_now(),
|
||||
(uint32_t)d->pramdac.core_clock_freq, // TODO : Research how this can be updated to accept uint64_t
|
||||
NANOSECONDS_PER_SECOND), // Was CLOCKS_PER_SEC
|
||||
SCALE_S_IN_US), // Was CLOCKS_PER_SEC
|
||||
d->ptimer.denominator,
|
||||
d->ptimer.numerator);
|
||||
}
|
||||
|
@ -91,6 +87,13 @@ 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:
|
||||
|
@ -101,6 +104,7 @@ 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);
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
|
||||
#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>
|
||||
|
@ -58,6 +59,7 @@
|
|||
#include "core\hle\Intercept.hpp"
|
||||
#include "common/win32/Threads.h"
|
||||
#include "Logging.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#include "vga.h"
|
||||
#include "nv2a.h" // For NV2AState
|
||||
|
@ -128,6 +130,14 @@ 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;
|
||||
|
@ -319,8 +329,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()
|
||||
{
|
||||
|
@ -1097,25 +1107,27 @@ void NV2ADevice::UpdateHostDisplay(NV2AState *d)
|
|||
}
|
||||
|
||||
// TODO: Fix this properly
|
||||
static void nv2a_vblank_thread(NV2AState *d)
|
||||
template<bool should_update_hle>
|
||||
void nv2a_vblank_interrupt(void *opaque)
|
||||
{
|
||||
g_AffinityPolicy->SetAffinityOther();
|
||||
CxbxSetThreadName("Cxbx NV2A VBLANK");
|
||||
auto nextVBlankTime = GetNextVBlankTime();
|
||||
NV2AState *d = static_cast<NV2AState *>(opaque);
|
||||
|
||||
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();
|
||||
if (!d->exiting) [[likely]] {
|
||||
d->pcrtc.pending_interrupts |= NV_PCRTC_INTR_0_VBLANK;
|
||||
update_irq(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);
|
||||
// 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]);
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1189,8 +1201,8 @@ void NV2ADevice::Init()
|
|||
d->vram_ptr = (uint8_t*)PHYSICAL_MAP_BASE;
|
||||
d->vram_size = g_SystemMaxMemory;
|
||||
|
||||
d->pramdac.core_clock_coeff = 0x00011c01; /* 189MHz...? */
|
||||
d->pramdac.core_clock_freq = 189000000;
|
||||
d->pramdac.core_clock_coeff = 0x00011C01; /* 233MHz...? */
|
||||
d->pramdac.core_clock_freq = 233333324;
|
||||
d->pramdac.memory_clock_coeff = 0;
|
||||
d->pramdac.video_clock_coeff = 0x0003C20D; /* 25182Khz...? */
|
||||
|
||||
|
@ -1202,7 +1214,13 @@ void NV2ADevice::Init()
|
|||
pvideo_init(d);
|
||||
}
|
||||
|
||||
vblank_thread = std::thread(nv2a_vblank_thread, d);
|
||||
d->vblank_last = get_now();
|
||||
if (bLLE_GPU) {
|
||||
d->vblank_cb = nv2a_vblank_interrupt<false>;
|
||||
}
|
||||
else {
|
||||
d->vblank_cb = nv2a_vblank_interrupt<true>;
|
||||
}
|
||||
|
||||
qemu_mutex_init(&d->pfifo.pfifo_lock);
|
||||
qemu_cond_init(&d->pfifo.puller_cond);
|
||||
|
@ -1227,9 +1245,8 @@ 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); // Cbxbx addition
|
||||
qemu_mutex_destroy(&d->pfifo.pfifo_lock); // Cxbxr addition
|
||||
if (d->pgraph.opengl_enabled) {
|
||||
vblank_thread.join();
|
||||
pvideo_destroy(d);
|
||||
}
|
||||
|
||||
|
@ -1377,3 +1394,45 @@ 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;
|
||||
}
|
||||
|
|
|
@ -108,6 +108,10 @@ 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
Loading…
Add table
Reference in a new issue