Compare commits

...

90 Commits

Author SHA1 Message Date
Luke Usher cf9a2b2b6b
Merge pull request #2495 from RadWolfie/d3d8ltcg-patches
D3D8LTCG: Add missing patches according to XbSymbolDatabase's symbols found
2025-08-01 11:03:52 +01:00
RadWolfie 540f7a9276 d3d8ltcg: add D3DDevice_LightEnable_4__LTCG_eax1 patch 2025-07-25 20:09:33 -05:00
RadWolfie 433ef5c7f2 d3d8ltcg: add D3DDevice_SetVertexShaderConstantNotInline_0__LTCG_ebx1_edx2_eax3 patch 2025-07-25 20:09:33 -05:00
RadWolfie 75e7c680a0 d3d8ltcg: add D3DDevice_MultiplyTransform_0__LTCG_ebx1_eax2 2025-07-25 20:09:33 -05:00
RadWolfie f7fb1c854a d3d8ltcg: add D3DDevice_GetBackBuffer_8__LTCG_eax1 patch 2025-07-25 20:09:32 -05:00
RadWolfie d02afcd668 d3d8ltcg: add D3DDevice_RunPushBuffer_4__LTCG_eax2 patch 2025-07-25 20:09:32 -05:00
RadWolfie 5241cd58ce d3d8ltcg: add D3DDevice_RunVertexStateShader_4__LTCG_esi2 patch 2025-07-25 20:09:32 -05:00
RadWolfie da8d26af40 d3d8ltcg: add D3DDevice_UpdateOverlay_16__LTCG_eax2 patch 2025-07-25 20:09:32 -05:00
RadWolfie b446452feb d3d8ltcg: add D3DDevice_EnableOverlay_0__LTCG patch 2025-07-25 20:09:31 -05:00
RadWolfie a27e7eeabd d3d8ltcg: add Lock3DSurface_16__LTCG_eax4 patch 2025-07-25 20:09:31 -05:00
RadWolfie 9f7f212ef6 d3d8ltcg: add Lock2DSurface_16__LTCG_esi4_eax5 patch 2025-07-25 00:12:17 -05:00
Luke Usher a659fcae89
Merge pull request #2493 from RadWolfie/d3d8-unpatches
Unpatch D3DDevice_SetFlickerFilter and D3DDevice_SetSoftDisplayFilter
2025-07-23 22:43:17 +01:00
RadWolfie 1b155413e3 d3d8: unpatch D3DDevice_SetFlickerFilter and D3DDevice_SetSoftDisplayFilter 2025-07-23 16:17:19 -05:00
Luke Usher 1e624e8a2b
Merge pull request #2492 from RadWolfie/update-xbsdb
Sync with XbSymbolDatabase changes for APIs and symbols rename
2025-07-23 22:01:32 +01:00
RadWolfie 0d07644876 jvs: use XbSymbolDatabase's symbol cache to get JVS_g_pPINS(A|B) directly 2025-07-23 15:19:39 -05:00
RadWolfie da5174784f lib: sync with XbSymbolDatabase changes for APIs and symbols rename 2025-07-23 14:36:14 -05:00
Luke Usher 2b19c8a539
Merge pull request #2491 from Margen67/cmake
cmake: Raise cmake_minimum_required to 3.12
2025-07-11 16:23:48 +01:00
Luke Usher 94da604981 fix more missing chrono includes 2025-07-11 13:31:35 +01:00
Luke Usher 19d7ef287b fix build on vs2022 2025-07-11 12:44:53 +01:00
Luke Usher 1e3e88bbd8 Updated SDL submodule to version 2.30.11 (again) 2025-07-11 11:24:28 +01:00
Luke Usher d1bd03a489
Merge pull request #2470 from Margen67/ci
CI: Upgrade to VS2022, update actions
2025-07-11 10:59:42 +01:00
Margen67 2bbad1582b cmake: Raise cmake_minimum_required to 3.12 2025-07-11 02:13:13 -07:00
Margen67 c64a077963 CI: Upgrade to VS2022, update actions 2025-07-11 01:54:52 -07:00
Luke Usher ec9934af2d
Merge pull request #2488 from RadWolfie/readme-update
Add minimum Windows SDK requirement or later note to the readme file
2025-04-13 18:35:50 +01:00
Luke Usher ce15f50848
Merge pull request #2490 from RadWolfie/toggle-console-mode
GUI: Add ability to choose console type
2025-04-13 18:35:10 +01:00
RadWolfie b1df891433 gui: add popup message when emulation is currently running 2025-04-13 11:21:34 -05:00
RadWolfie 5355e08b30 kernel: fix misplaced block of chihiro code to the respective location to allow forced console type by user selection on boot 2025-04-13 11:21:34 -05:00
RadWolfie 7762e883a8 gui: add selection for auto and manual console type 2025-04-13 11:21:34 -05:00
ergo720 cada16125d
Merge pull request #2489 from RadWolfie/debugger-deprecate
debugger: mark tool as deprecate
2025-04-10 22:38:44 +02:00
RadWolfie eecc1ac1f1 readme: add minimum Windows SDK requirement or later note according to #2473 pull request 2025-04-10 08:07:16 -05:00
RadWolfie 2d6696451b debugger: mark tool as deprecate 2025-04-10 07:59:30 -05:00
ergo720 dd36dd598c
Merge pull request #2484 from ergo720/update_sdl
Updated SDL submodule to version 2.30.11
2025-03-31 13:08:49 +02:00
ergo720 87634a2e27 Updated SDL submodule to version 2.30.11 2025-03-30 10:27:23 +02:00
Luke Usher 6f32d89545
Merge pull request #2474 from Margen67/build
cmake: Replace /Ob2 with /Ob3
2024-12-23 08:55:15 +00:00
Margen67 ec0c288bc4 cmake: Replace /Ob2 with /Ob3
See https://learn.microsoft.com/en-us/cpp/build/reference/ob-inline-function-expansion
2024-12-17 00:34:09 -08:00
RadWolfie 8bfbcb56fd
Merge pull request #2473 from Margen67/w11
Disable rounded corners on Windows 11
2024-12-17 01:59:01 -06:00
Margen67 8965d2443b Remove rounded corners on Windows 11 2024-12-16 23:48:10 -08:00
RadWolfie b33ed95c5b
Merge pull request #2472 from Margen67/ci2
CI: Upgrade actions
2024-12-16 22:15:06 -06:00
Margen67 8ee17b512c CI: Update actions 2024-12-16 19:39:48 -08:00
RadWolfie 50334cbc31
Merge pull request #2469 from Margen67/subhook
Replace subhook with working mirror
2024-12-13 14:52:09 -06:00
Margen67 41454b8c26 Replace subhook with working mirror 2024-12-13 12:41:38 -08:00
Luke Usher 204dcf8801
Merge pull request #2462 from RadWolfie/file-minor-fixes
File minor fixes
2024-08-25 10:21:29 +01:00
RadWolfie 77c63ceec3 kernel: fix dashupdate titles attempt delete new files
NOTE: Partition2.bin needs to be emulated in order for copy files to partition and fatx metadata.
2024-08-18 08:39:16 -05:00
RadWolfie 2cfaba893e kernel: fix Exhibition Demo discs problem for copy soundtracks onto hdd (require force santion) 2024-08-18 08:39:16 -05:00
Luke Usher 17b0cb81d4 CI: Specify minimum platform and SDK version 2024-07-08 15:19:54 +01:00
RadWolfie daa6a816ff Merge experimental chihiro branch 2024-07-05 12:19:54 -05:00
Luke Usher 6caf3ea679 chihiro: prevent JVS register updates from being missed due to long delays
This really needs a better solution, but for now, this will do.
2024-07-05 12:19:54 -05:00
Luke Usher 9a58823b70 chihiro: emulate a chihiro system when boot.id is present 2024-07-05 12:19:54 -05:00
Luke Usher 3edd8d168b chihiro: fix an issue where media board detection failed due to instant response time 2024-07-05 12:19:54 -05:00
RadWolfie f894d31332 Cleanly rebase chihiro-work on develop
Co-authored-by: Luke Usher <luke.usher@outlook.com>
Co-authored-by: wutno <aaron@installgentoo.net>
Co-authored-by: RadWolfie <RadWolfie@users.noreply.github.com>
2024-07-05 12:19:54 -05:00
RadWolfie 9241bec768 Merge ergo720 less_busy_loops branch 2024-07-05 12:19:53 -05:00
RadWolfie c50a0c5c7d Merge EmuX86 passive branch 2024-07-05 12:18:57 -05:00
Luke Usher 87bab04932 EmuX86: Let invalid memory accesses trigger a warning rather than a fatal error
This seems to resolve most regressions we have had in recent history.
2024-07-05 12:18:54 -05:00
ergo720 ad6769bbf3 Never change the thread priority on the host and the disable boost flag too
This fixes almost all the games that were broken in this branch
2024-07-05 10:58:05 -05:00
ergo720 0e63131fc3 Use a DPC for expired timers + don't execute NV2A DPCs from the timer thread to avoid the exception overhead 2024-07-05 10:58:05 -05:00
ergo720 889040c56a Fixed an issue in WaitApc where the wait block was not removed when using a zero timeout or when satisfied by a user APC + properly lock the wait block operations to avoid a race between SatisfyWait and KiTimerExpiration 2024-07-05 10:58:05 -05:00
ergo720 86542c9f2e Implemented PTIMER alarm interrupt of NV2A + fixed a bug in timer_init
This fixes DOAU showing the dirty disk error in PAL50 mode
2024-07-05 10:58:05 -05:00
ergo720 c9edbd1003 Fixed wrong nv2a clock frequency
This is accessed by DOAU via PTIMER only in PAL50 mode
2024-07-05 10:58:05 -05:00
ergo720 ebb122f2a0 Fixed a bug in KeTickCount + check all timer indices when we are late in KiClockIsr
This almost completely fixes the slowness in Panzer Dragoon Orta
2024-07-05 10:58:04 -05:00
ergo720 c158a472ff Make sure to reset WaitStatus when a new wait starts
This fixes an issue in Panzer Dragoon Orta, where KeDelayExecutionThread would return X_STATUS_TIMEOUT | X_STATUS_USER_APC
2024-07-05 10:58:04 -05:00
ergo720 6961d1c7a1 Make sure that GetNativeHandle succeeds before attempting to get the native handle
This fixes a sporadic crash in Panzer Dragoon Orta, where the title calls KeSetBasePriorityThread on a thread that has already terminated
2024-07-05 10:58:04 -05:00
ergo720 2f7cfe7e95 Fixed a bug in KiInsertTimerTable + log all objects being waited on in NtWaitForMultipleObjectsEx
This fixes a crash in Metal Slug 3
2024-07-05 10:58:04 -05:00
ergo720 46d0173673 Account for partial milliseconds in KiClockIsr
This fixes the slowness in The lord of the rings: the third era
2024-07-05 10:58:04 -05:00
ergo720 c7b028b3e7 Fixed a race condition in WaitApc + removed wrong InsertTailList for ktimers used during a timeout
This fixes almost all broken games in this branch. Still broken: PDO: 1 fps vs 10 fps, DOA3: freezes after title screen, Lord of the rings The third era: Unable to determine default Xbox backbuffer error???
2024-07-05 10:58:04 -05:00
ergo720 3d12edc77d Always create a wait object even when we satisfy the wait on the host side + fixed a bug in KiWaitTestNoYield
This fixes an occasionl freeze in Steel Battalion + the slowness in JSRF
2024-07-05 10:58:04 -05:00
ergo720 08ab4b9164 Revert to using the host to do thread suspension 2024-07-05 10:58:04 -05:00
ergo720 4fca5c7007 Hack: <= thread priority instead of >= 2024-07-05 10:58:04 -05:00
ergo720 e26f20108a Setup a KTIMER for the other functions using WaitApc too 2024-07-05 10:58:04 -05:00
ergo720 8475124e5b Restore single interrupt loop in update_non_periodic_events 2024-07-05 10:58:03 -05:00
ergo720 9b2ae106e5 Place nvnet in its own thread 2024-07-05 10:58:03 -05:00
ergo720 b3bfeca3a8 Use get_now directly in system_events instead of qpc 2024-07-05 10:58:03 -05:00
ergo720 b77a13b708 Adjust KeSystemTime when the host system time is changed by the user 2024-07-05 10:58:03 -05:00
ergo720 1b5e111ae3 Account for delays between calls to KiClockIsr
This fixes the slowness in the dashboard
2024-07-05 10:58:03 -05:00
ergo720 1504a75a46 Raise priority of system events thread 2024-07-05 10:58:03 -05:00
ergo720 87496ab873 Removed delta amount added to KeSystemTime 2024-07-05 10:58:03 -05:00
ergo720 5b37a7ec21 Fixed thread order initialization when a thread starts suspended 2024-07-05 10:58:03 -05:00
ergo720 639f42c318 Make sure to hold the DPC lock until the DPC list has been emptied
This fixes a crash in Lord of the rings: The fellowship of the ring
2024-07-05 10:58:03 -05:00
ergo720 8d92992a6b Implemented kernel unwait routines + updated/fixed KeWaitForMultipleObjects and KeWaitForSingleObject 2024-07-05 10:58:02 -05:00
ergo720 7323eed73e Only change the priority of a thread if it is being set above normal 2024-07-05 10:58:02 -05:00
ergo720 b47c1f195c Unpatch D3DDevice_BlockUntilVerticalBlank and D3DDevice_SetVerticalBlankCallback 2024-07-05 10:58:02 -05:00
ergo720 7c73bfc525 Avoid triggering multiple gpu interrupts outside the vblank 2024-07-05 10:58:02 -05:00
ergo720 1b4a3bb54f Moved position of ObfDereferenceObject in NtSuspendThread 2024-07-05 10:58:02 -05:00
ergo720 750d202fa8 Removed scaling hack in KeInterruptTime and KeTickCount + added yield in system_events routine
This fixes the stuttering in Halo 2, Metal slug 3, JSRF and restores PDO, PSO to the same state as in master
2024-07-05 10:58:02 -05:00
ergo720 e7bca5e1bf Implemented suspend/resume kernel Nt routines with the corresponding Ke routines 2024-07-05 10:58:01 -05:00
ergo720 937ab9e1c2 Fixed a bug in KeSetBasePriorityThread 2024-07-05 10:58:01 -05:00
ergo720 8006f55cf3 Merge many different periodic events in a single thread, instead of each having its own busy loop
This merges vblank, ohci's eof, pit interrupt, dsound sync and async workers, nvnet packet processing and system interrupt
2024-07-05 10:58:01 -05:00
ergo720 1828ddfd6f Merge lle and hle vblank routines in a single thread 2024-07-05 10:58:00 -05:00
ergo720 bc42cfaa6b Removed unnecessary lock in the interrupt thread 2024-07-05 10:58:00 -05:00
PatrickvL b1235b7733 Merge pull request #2458 from LukeUsher/fix-compilation-vs2022 2024-07-05 10:41:18 -05:00
Luke Usher 1615ecc976 fix the build on vs2022 17.9.1 2024-05-22 12:46:03 +01:00
80 changed files with 4307 additions and 1332 deletions

112
.github/labeler.yml vendored
View File

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

View File

@ -22,30 +22,29 @@ on:
jobs: jobs:
build-windows: build-windows:
name: Build (Windows, ${{ matrix.configuration }}, VS${{ matrix.vsver }}) # runner.os doesn't work here name: Build (Windows, ${{ matrix.configuration }}) # runner.os doesn't work here
runs-on: windows-${{ matrix.vsver }} runs-on: windows-2025
env: env:
POWERSHELL_TELEMETRY_OPTOUT: 1 POWERSHELL_TELEMETRY_OPTOUT: 1
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
configuration: [Release, Debug] configuration: [Release, Debug]
vsver: [2022]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: Generate CMake files - name: Generate CMake files
run: cmake -B build -A Win32 run: cmake -B build -A Win32 -DBUILD_CXBXR_DEBUGGER=ON -DCMAKE_SYSTEM_VERSION=7
- name: Build - name: Build
run: cmake --build build --config ${{ matrix.configuration }} -j $env:NUMBER_OF_PROCESSORS run: cmake --build build --config ${{ matrix.configuration }} -j $env:NUMBER_OF_PROCESSORS
- name: Prepare artifacts - name: Prepare artifacts
if: matrix.configuration == 'Release' if: matrix.configuration == 'Release'
run: cmake --install build --config ${{ matrix.configuration }} --prefix artifacts run: cmake --install build --config ${{ matrix.configuration }} --prefix artifacts
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
if: matrix.configuration == 'Release' if: matrix.configuration == 'Release'
with: with:
name: CxbxReloaded-${{ matrix.configuration }}-VS${{ matrix.vsver }} name: CxbxReloaded-${{ matrix.configuration }}
path: artifacts/bin path: artifacts/bin
if-no-files-found: error if-no-files-found: error
@ -59,7 +58,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Download artifacts - name: Download artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
path: artifacts path: artifacts
- name: Re-zip artifacts - name: Re-zip artifacts

View File

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

View File

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

6
.gitmodules vendored
View File

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

View File

@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.8) cmake_minimum_required (VERSION 3.12)
project(Cxbx-Reloaded) project(Cxbx-Reloaded)
@ -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/SDL2" EXCLUDE_FROM_ALL)
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/mio" EXCLUDE_FROM_ALL)
# Cxbx-Reloaded projects # Cxbx-Reloaded projects
set(CXBXR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}) set(CXBXR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR})
@ -346,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/XbInternalDSVoice.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/DSOUND/common/XbInternalStruct.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/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/Patches.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/XACTENG/XactEng.cpp" "${CXBXR_ROOT_DIR}/src/core/hle/XACTENG/XactEng.cpp"
"${CXBXR_ROOT_DIR}/src/core/hle/XGRAPHIC/XGraphic.cpp" "${CXBXR_ROOT_DIR}/src/core/hle/XGRAPHIC/XGraphic.cpp"
@ -376,6 +379,8 @@ file (GLOB CXBXR_SOURCE_EMU
"${CXBXR_ROOT_DIR}/src/core/kernel/support/NativeHandle.cpp" "${CXBXR_ROOT_DIR}/src/core/kernel/support/NativeHandle.cpp"
"${CXBXR_ROOT_DIR}/src/core/kernel/support/PatchRdtsc.cpp" "${CXBXR_ROOT_DIR}/src/core/kernel/support/PatchRdtsc.cpp"
"${CXBXR_ROOT_DIR}/src/devices/ADM1032Device.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/EEPROMDevice.cpp"
"${CXBXR_ROOT_DIR}/src/devices/network/NVNetDevice.cpp" "${CXBXR_ROOT_DIR}/src/devices/network/NVNetDevice.cpp"
"${CXBXR_ROOT_DIR}/src/devices/MCPXDevice.cpp" "${CXBXR_ROOT_DIR}/src/devices/MCPXDevice.cpp"
@ -425,6 +430,12 @@ file (GLOB CXBXR_SOURCE_EMU
"${CXBXR_ROOT_DIR}/src/common/ReserveAddressRanges.cpp" "${CXBXR_ROOT_DIR}/src/common/ReserveAddressRanges.cpp"
) )
option(BUILD_CXBXR_DEBUGGER "Build cxbxr-debugger tool (with cheat table support)")
if(BUILD_CXBXR_DEBUGGER)
message(DEPRECATION "The Cxbxr-Debugger tool will eventually be removed from the upstream branch.")
endif()
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/cxbx") add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/cxbx")
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/cxbxr-ldr") add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/projects/cxbxr-ldr")
@ -449,7 +460,7 @@ endif()
# Check if generator is Visual Studio then enable Cxbxr-Debugger project. # Check if generator is Visual Studio then enable Cxbxr-Debugger project.
# Since C# is currently supported with Visual Studio for now. # Since C# is currently supported with Visual Studio for now.
if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|[9][0-9])") if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|[9][0-9])" AND BUILD_CXBXR_DEBUGGER)
# Issues with compile (the same with develop branch) and # Issues with compile (the same with develop branch) and
# for some reason did not put the files into virtual folder? # for some reason did not put the files into virtual folder?
# Might need to put the list in the source folder for workaround fix. # Might need to put the list in the source folder for workaround fix.

View File

@ -89,6 +89,7 @@ Don't open `CMakeLists.txt` from Visual Studio, as it won't generate files in th
1. [Visual Studio](https://visualstudio.microsoft.com/downloads/) 2022 1. [Visual Studio](https://visualstudio.microsoft.com/downloads/) 2022
* C++ and C# desktop development * C++ and C# desktop development
* Windows Universal CRT SDK * Windows Universal CRT SDK
* Windows 11 SDK (10.0.22621.0) or later
* C++ CMake tools for Windows * C++ CMake tools for Windows
* *Optional if CMake is installed* * *Optional if CMake is installed*
* [Microsoft Child Process Debugging Power Tool](https://marketplace.visualstudio.com/items?itemName=vsdbgplat.MicrosoftChildProcessDebuggingPowerTool) * [Microsoft Child Process Debugging Power Tool](https://marketplace.visualstudio.com/items?itemName=vsdbgplat.MicrosoftChildProcessDebuggingPowerTool)
@ -96,10 +97,11 @@ Don't open `CMakeLists.txt` from Visual Studio, as it won't generate files in th
##### Generate Visual Studio files ##### Generate Visual Studio files
1. If you don't have CMake installed, open `___ Native Tools Command Prompt for VS 20##`. 1. If you don't have CMake installed, open `___ Native Tools Command Prompt for VS 20##`.
2. `cd` to the Cxbx-Reloaded directory. 2. `cd` to the Cxbx-Reloaded directory.
3. Run these commands. 3. Run the following command: `cmake -B build -G "Visual Studio 17 2022" -A Win32` \
1. `mkdir build & cd build` **NOTES**:
2. `cmake .. -G "Visual Studio 17 2022" -A Win32` * VS2022 17.0 or later is required.
* VS2022 17.0 or later is required. * To build the Cxbx-Reloaded Debugger tool, add the variable `-DBUILD_CXBXR_DEBUGGER=ON` to the above command.
* _This debugger tool is deprecated and will be eventually removed, please use the Visual Studio debugger instead._
4. Open `Cxbx-Reloaded.sln` from the `build` directory. 4. Open `Cxbx-Reloaded.sln` from the `build` directory.
5. Select the Release configuration, then click Build. 5. Select the Release configuration, then click Build.
* Debug builds are **significantly slower, and only for developers**. * Debug builds are **significantly slower, and only for developers**.

2
import/SDL2 vendored

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

@ -1 +1 @@
Subproject commit 774111351210e6f340246d6fb32741b09708f381 Subproject commit 6de71a326b094377f8773817e1f228a4e435496a

1
import/mio vendored Submodule

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.8) cmake_minimum_required (VERSION 3.12)
project(imgui LANGUAGES CXX) project(imgui LANGUAGES CXX)
# Since imgui doesn't have CMake, we'll make an interface project here. # Since imgui doesn't have CMake, we'll make an interface project here.

View File

@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.8) cmake_minimum_required (VERSION 3.12)
project(libtomcrypt) project(libtomcrypt)
# Suppress extra stuff from generated solution # Suppress extra stuff from generated solution

View File

@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.8) cmake_minimum_required (VERSION 3.12)
project(libtommath) project(libtommath)
# Suppress extra stuff from generated solution # Suppress extra stuff from generated solution

View File

@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.8) cmake_minimum_required (VERSION 3.12)
project(libusb LANGUAGES CXX) project(libusb LANGUAGES CXX)
# Since libusb doesn't have CMake, we'll make an interface project here. # Since libusb doesn't have CMake, we'll make an interface project here.

View File

@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.8) cmake_minimum_required (VERSION 3.12)
project(cxbxr-debugger LANGUAGES CSharp) project(cxbxr-debugger LANGUAGES CSharp)
# Output all binary files into one folder # Output all binary files into one folder

View File

@ -41,6 +41,7 @@
this.windowsMenu = new System.Windows.Forms.ToolStripMenuItem(); this.windowsMenu = new System.Windows.Forms.ToolStripMenuItem();
this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.statusStrip1 = new System.Windows.Forms.StatusStrip(); this.statusStrip1 = new System.Windows.Forms.StatusStrip();
this.lblStatusDeprecate = new System.Windows.Forms.ToolStripStatusLabel();
this.lblStatus = new System.Windows.Forms.ToolStripStatusLabel(); this.lblStatus = new System.Windows.Forms.ToolStripStatusLabel();
this.menuStrip.SuspendLayout(); this.menuStrip.SuspendLayout();
this.statusStrip1.SuspendLayout(); this.statusStrip1.SuspendLayout();
@ -132,18 +133,29 @@
// //
this.statusStrip1.ImageScalingSize = new System.Drawing.Size(24, 24); this.statusStrip1.ImageScalingSize = new System.Drawing.Size(24, 24);
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.lblStatusDeprecate,
this.lblStatus}); this.lblStatus});
this.statusStrip1.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.VerticalStackWithOverflow;
this.statusStrip1.Location = new System.Drawing.Point(0, 339); this.statusStrip1.Location = new System.Drawing.Point(0, 339);
this.statusStrip1.Name = "statusStrip1"; this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Padding = new System.Windows.Forms.Padding(1, 0, 9, 0); this.statusStrip1.Padding = new System.Windows.Forms.Padding(1, 0, 9, 0);
this.statusStrip1.Size = new System.Drawing.Size(734, 22); this.statusStrip1.Size = new System.Drawing.Size(734, 61);
this.statusStrip1.TabIndex = 2; this.statusStrip1.TabIndex = 2;
this.statusStrip1.Text = "statusStrip1"; this.statusStrip1.Text = "statusStrip1";
// //
// lblStatusDeprecate
//
this.lblStatusDeprecate.BackColor = System.Drawing.Color.Yellow;
this.lblStatusDeprecate.Name = "lblStatusDeprecate";
this.lblStatusDeprecate.Size = new System.Drawing.Size(723, 15);
this.lblStatusDeprecate.Spring = true;
this.lblStatusDeprecate.Text = "WARNING: cxbxr-debugger will eventually be removed from upstream branch.";
this.lblStatusDeprecate.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// lblStatus // lblStatus
// //
this.lblStatus.Name = "lblStatus"; this.lblStatus.Name = "lblStatus";
this.lblStatus.Size = new System.Drawing.Size(39, 17); this.lblStatus.Size = new System.Drawing.Size(723, 15);
this.lblStatus.Text = "Ready"; this.lblStatus.Text = "Ready";
// //
// CxbxDebuggerMain // CxbxDebuggerMain
@ -180,6 +192,7 @@
private System.Windows.Forms.ToolStripMenuItem miSuspend; private System.Windows.Forms.ToolStripMenuItem miSuspend;
private System.Windows.Forms.ToolStripMenuItem miResume; private System.Windows.Forms.ToolStripMenuItem miResume;
private System.Windows.Forms.StatusStrip statusStrip1; private System.Windows.Forms.StatusStrip statusStrip1;
private System.Windows.Forms.ToolStripStatusLabel lblStatusDeprecate;
private System.Windows.Forms.ToolStripStatusLabel lblStatus; private System.Windows.Forms.ToolStripStatusLabel lblStatus;
} }
} }

View File

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

View File

@ -90,6 +90,7 @@ static struct {
const char* DataCustomLocation = "DataCustomLocation"; const char* DataCustomLocation = "DataCustomLocation";
const char* IgnoreInvalidXbeSig = "IgnoreInvalidXbeSig"; const char* IgnoreInvalidXbeSig = "IgnoreInvalidXbeSig";
const char *IgnoreInvalidXbeSec = "IgnoreInvalidXbeSec"; const char *IgnoreInvalidXbeSec = "IgnoreInvalidXbeSec";
const char* ConsoleTypeToggle = "ConsoleTypeToggle";
} sect_gui_keys; } sect_gui_keys;
static const char* section_core = "core"; static const char* section_core = "core";
@ -342,6 +343,8 @@ bool Settings::LoadConfig()
m_gui.bIgnoreInvalidXbeSig = m_si.GetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSig, /*Default=*/false); m_gui.bIgnoreInvalidXbeSig = m_si.GetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSig, /*Default=*/false);
m_gui.bIgnoreInvalidXbeSec = m_si.GetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSec, /*Default=*/false); m_gui.bIgnoreInvalidXbeSec = m_si.GetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSec, /*Default=*/false);
m_gui.ConsoleTypeToggle = (EMU_CONSOLE_TYPE)m_si.GetLongValue(section_gui, sect_gui_keys.ConsoleTypeToggle, /*Default=*/EMU_CONSOLE_TYPE_AUTO);
// ==== GUI End ============= // ==== GUI End =============
// ==== Core Begin ========== // ==== Core Begin ==========
@ -588,6 +591,8 @@ bool Settings::Save(std::string file_path)
m_si.SetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSig, m_gui.bIgnoreInvalidXbeSig, nullptr, true); m_si.SetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSig, m_gui.bIgnoreInvalidXbeSig, nullptr, true);
m_si.SetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSec, m_gui.bIgnoreInvalidXbeSec, nullptr, true); m_si.SetBoolValue(section_gui, sect_gui_keys.IgnoreInvalidXbeSec, m_gui.bIgnoreInvalidXbeSec, nullptr, true);
m_si.SetLongValue(section_gui, sect_gui_keys.ConsoleTypeToggle, m_gui.ConsoleTypeToggle, nullptr, true, true);
// ==== GUI End ============= // ==== GUI End =============
// ==== Core Begin ========== // ==== Core Begin ==========

View File

@ -46,6 +46,14 @@ extern uint16_t g_LibVersion_DSOUND;
"Invalid "#type" size, please verify structure is align, not adding new member, or is using placeholder reserves." \ "Invalid "#type" size, please verify structure is align, not adding new member, or is using placeholder reserves." \
" Otherwise, please perform versioning upgrade and update "#type" sizeof check." " Otherwise, please perform versioning upgrade and update "#type" sizeof check."
// Toggle emulation console mode.
typedef enum _EMU_CONSOLE_TYPE {
EMU_CONSOLE_TYPE_AUTO = 0,
EMU_CONSOLE_TYPE_RETAIL = 1,
EMU_CONSOLE_TYPE_DEVKIT = 2,
EMU_CONSOLE_TYPE_CHIHIRO = 3,
} EMU_CONSOLE_TYPE;
// Cxbx-Reloaded's data storage location. // Cxbx-Reloaded's data storage location.
typedef enum _CXBX_DATA { typedef enum _CXBX_DATA {
CXBX_DATA_INVALID = -1, CXBX_DATA_INVALID = -1,
@ -91,6 +99,7 @@ public:
std::string szCustomLocation = ""; std::string szCustomLocation = "";
bool bIgnoreInvalidXbeSig; bool bIgnoreInvalidXbeSig;
bool bIgnoreInvalidXbeSec; bool bIgnoreInvalidXbeSec;
unsigned int ConsoleTypeToggle;
} m_gui; } m_gui;
// Core settings // Core settings

View File

@ -25,19 +25,46 @@
// * // *
// ****************************************************************** // ******************************************************************
#ifdef _WIN32 #include <core\kernel\exports\xboxkrnl.h>
#include <windows.h> #include <windows.h>
#endif #include <chrono>
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <mutex> #include <mutex>
#include <array>
#include "Timer.h" #include "Timer.h"
#include "common\util\CxbxUtil.h" #include "common\util\CxbxUtil.h"
#include "core\kernel\support\EmuFS.h" #include "core\kernel\support\EmuFS.h"
#include "core/kernel/exports/EmuKrnlPs.hpp" #include "core\kernel\exports\EmuKrnlPs.hpp"
#ifdef __linux__ #include "core\kernel\exports\EmuKrnl.h"
#include <time.h> #include "devices\Xbox.h"
#endif #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 // More precise sleep, but with increased CPU usage
void SleepPrecise(std::chrono::steady_clock::time_point targetTime) void SleepPrecise(std::chrono::steady_clock::time_point targetTime)
@ -69,174 +96,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. // NOTE: the pit device is not implemented right now, so we put this here
// See the QEMUClockType QEMU_CLOCK_VIRTUAL of XQEMU for more info. static uint64_t pit_next(uint64_t now)
#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)
{ {
#ifdef _WIN32 constexpr uint64_t pit_period = 1000;
uint64_t Ret = Timer_GetScaledPerformanceCounter(SCALE_S_IN_NS); uint64_t next = pit_last + pit_period;
#elif __linux__
static struct timespec ts; if (now >= next) {
clock_gettime(CLOCK_MONOTONIC_RAW, &ts); xbox::KiClockIsr(now - pit_last);
uint64_t Ret = Muldiv64(ts.tv_sec, SCALE_S_IN_NS, 1) + ts.tv_nsec; pit_last = get_now();
#else return pit_period;
#error "Unsupported OS" }
#endif
return Ret; return pit_last + pit_period - now; // time remaining until next clock interrupt
} }
// Calculates the next expire time of the timer static void update_non_periodic_events()
static inline uint64_t GetNextExpireTime(TimerObject* Timer)
{ {
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 uint64_t get_now()
void Timer_Destroy(TimerObject* Timer)
{ {
unsigned int index, i; LARGE_INTEGER now;
std::lock_guard<std::mutex>lock(TimerMtx); QueryPerformanceCounter(&now);
uint64_t elapsed_us = now.QuadPart - last_qpc;
index = TimerList.size(); last_qpc = now.QuadPart;
for (i = 0; i < index; i++) { elapsed_us *= 1000000;
if (Timer == TimerList[i]) { elapsed_us /= HostQPCFrequency;
index = i; exec_time += elapsed_us;
} return exec_time;
}
assert(index != TimerList.size());
delete Timer;
TimerList.erase(TimerList.begin() + index);
} }
void Timer_Shutdown() static uint64_t get_next(uint64_t now)
{ {
unsigned i, iXboxThreads = 0; std::array<uint64_t, 5> next = {
TimerMtx.lock(); pit_next(now),
g_NV2A->vblank_next(now),
for (i = 0; i < TimerList.size(); i++) { g_NV2A->ptimer_next(now),
TimerObject* Timer = TimerList[i]; g_USB0->m_HostController->OHCI_next(now),
// We only need to terminate host threads. dsound_next(now)
if (!Timer->IsXboxTimer) { };
Timer_Exit(Timer); return *std::min_element(next.begin(), next.end());
}
// 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();
} }
// Thread that runs the timer xbox::void_xt NTAPI system_events(xbox::PVOID arg)
void NTAPI ClockThread(void *TimerArg)
{ {
TimerObject *Timer = static_cast<TimerObject *>(TimerArg); // 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.
if (!Timer->Name.empty()) { // So we increase its priority to above normal, so that it scheduled more often
CxbxSetThreadName(Timer->Name.c_str()); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
}
if (!Timer->IsXboxTimer) {
g_AffinityPolicy->SetAffinityOther();
}
uint64_t NewExpireTime = GetNextExpireTime(Timer); // Always run this thread at dpc level to prevent it from ever executing APCs/DPCs
xbox::KeRaiseIrqlToDpcLevel();
while (true) { while (true) {
if (GetTime_NS(Timer) > NewExpireTime) { const uint64_t last_time = get_now();
if (Timer->Exit.load()) { const uint64_t nearest_next = get_next(last_time);
Timer_Destroy(Timer);
return; while (true) {
update_non_periodic_events();
uint64_t elapsed_us = get_now() - last_time;
if (elapsed_us >= nearest_next) {
break;
} }
Timer->Callback(Timer->Opaque); std::this_thread::yield();
NewExpireTime = GetNextExpireTime(Timer);
} }
Sleep(1); // prevent burning the cpu
} }
} }
// Changes the expire time of a timer
void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms)
{
Timer->ExpireTime_MS.store(Expire_ms);
}
// Destroys the timer
void Timer_Exit(TimerObject* Timer)
{
Timer->Exit.store(true);
}
// Allocates the memory for the timer object
TimerObject* Timer_Create(TimerCB Callback, void* Arg, std::string Name, bool IsXboxTimer)
{
std::lock_guard<std::mutex>lock(TimerMtx);
TimerObject* pTimer = new TimerObject;
pTimer->Type = CLOCK_REALTIME;
pTimer->Callback = Callback;
pTimer->ExpireTime_MS.store(0);
pTimer->Exit.store(false);
pTimer->Opaque = Arg;
pTimer->Name = Name.empty() ? "Unnamed thread" : std::move(Name);
pTimer->IsXboxTimer = IsXboxTimer;
TimerList.emplace_back(pTimer);
return pTimer;
}
// Starts the timer
// Expire_MS must be expressed in NS
void Timer_Start(TimerObject* Timer, uint64_t Expire_MS)
{
Timer->ExpireTime_MS.store(Expire_MS);
if (Timer->IsXboxTimer) {
xbox::HANDLE hThread;
CxbxrCreateThread(&hThread, xbox::zeroptr, ClockThread, Timer, FALSE);
}
else {
std::thread(ClockThread, Timer).detach();
}
}
// Retrives the frequency of the high resolution clock of the host
void Timer_Init()
{
#ifdef _WIN32
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&HostQPCFrequency));
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&HostQPCStartTime));
#elif __linux__
ClockFrequency = 0;
#else
#error "Unsupported OS"
#endif
}
int64_t Timer_GetScaledPerformanceCounter(int64_t Period) int64_t Timer_GetScaledPerformanceCounter(int64_t Period)
{ {
LARGE_INTEGER currentQPC; LARGE_INTEGER currentQPC;

View File

@ -40,33 +40,12 @@
#define SCALE_MS_IN_US 1000 #define SCALE_MS_IN_US 1000
#define SCALE_US_IN_US 1 #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; extern int64_t HostQPCFrequency;
/* Timer exported functions */ void timer_init();
TimerObject* Timer_Create(TimerCB Callback, void* Arg, std::string Name, bool IsXboxTimer); uint64_t get_now();
void Timer_Start(TimerObject* Timer, uint64_t Expire_MS);
void Timer_Exit(TimerObject* Timer);
void Timer_ChangeExpireTime(TimerObject* Timer, uint64_t Expire_ms);
uint64_t GetTime_NS(TimerObject* Timer);
void Timer_Init();
void Timer_Shutdown();
int64_t Timer_GetScaledPerformanceCounter(int64_t Period); int64_t Timer_GetScaledPerformanceCounter(int64_t Period);
void SleepPrecise(std::chrono::steady_clock::time_point targetTime); void SleepPrecise(std::chrono::steady_clock::time_point targetTime);
#endif #endif

View File

@ -121,9 +121,6 @@ bool HandleFirstLaunch()
} }
// NOTE: Require to be after g_renderbase's shutdown process. // 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! // NOTE: Must be last step of shutdown process and before CxbxUnlockFilePath call!
// Shutdown the memory manager // Shutdown the memory manager
g_VMManager.Shutdown(); g_VMManager.Shutdown();

View File

@ -33,6 +33,7 @@
#include "EmuShared.h" #include "EmuShared.h"
#include "Logging.h" #include "Logging.h"
#include <future> #include <future>
#include <chrono>
constexpr ControlState INPUT_DETECT_THRESHOLD = 0.35; // NOTE: this should probably be made user configurable constexpr ControlState INPUT_DETECT_THRESHOLD = 0.35; // NOTE: this should probably be made user configurable

View File

@ -26,6 +26,7 @@
#ifndef CXBXUTIL_H #ifndef CXBXUTIL_H
#define CXBXUTIL_H #define CXBXUTIL_H
#include <algorithm>
#include <stdexcept> #include <stdexcept>
#include "xbox_types.h" #include "xbox_types.h"
#include "Cxbx.h" #include "Cxbx.h"

View File

@ -60,6 +60,15 @@ bool hasKey(std::string key)
return false; return false;
} }
// Delete the key if it exist
void DeleteKey(std::string key)
{
auto found = g_cli_configs.find(key);
if (found != g_cli_configs.end()) {
g_cli_configs.erase(found);
}
}
// Generic getter // Generic getter
bool GetValue(const std::string key, std::string* value) bool GetValue(const std::string key, std::string* value)
{ {
@ -179,4 +188,12 @@ void SetSystemType(const std::string value)
} }
} }
void ClearSystemType()
{
// Clear any system types key existence.
DeleteKey(cli_config::system_retail);
DeleteKey(cli_config::system_devkit);
DeleteKey(cli_config::system_chihiro);
}
} }

View File

@ -57,5 +57,6 @@ long long GetSessionID();
void SetLoad(const std::string value); void SetLoad(const std::string value);
void SetSystemType(const std::string value); void SetSystemType(const std::string value);
void ClearSystemType();
} }

View File

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

View File

@ -72,7 +72,7 @@ Xbe::Xbe(const char *x_szFilename)
// This is necessary because CxbxInitWindow internally calls g_AffinityPolicy->SetAffinityOther. If we are launched directly from the command line and the dashboard // 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 // cannot be opened, we will crash below because g_AffinityPolicy will be empty
g_AffinityPolicy = AffinityPolicy::InitPolicy(); g_AffinityPolicy = AffinityPolicy::InitPolicy();
CxbxInitWindow(false); CxbxInitWindow();
ULONG FatalErrorCode = FATAL_ERROR_XBE_DASH_GENERIC; ULONG FatalErrorCode = FATAL_ERROR_XBE_DASH_GENERIC;

File diff suppressed because it is too large Load Diff

View File

@ -4256,3 +4256,74 @@ HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShaderInput)
return 0; 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;
}
// LTCG specific D3DDevice_SetFlickerFilter function...
// This uses a custom calling convention where parameter is passed in ESI
// Test-case: Metal Wolf Chaos
__declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetFlickerFilter_0__LTCG_esi1)
(
)
{
dword_xt Filter;
__asm {
LTCG_PROLOGUE
mov Filter, esi
}
EMUPATCH(D3DDevice_SetFlickerFilter)(Filter);
__asm {
LTCG_EPILOGUE
ret
}
}
// ******************************************************************
// * patch: D3DDevice_SetFlickerFilter
// ******************************************************************
void WINAPI xbox::EMUPATCH(D3DDevice_SetFlickerFilter)
(
dword_xt Filter
)
{
LOG_FUNC_ONE_ARG(Filter);
LOG_IGNORED();
}
// ******************************************************************
// * patch: D3DDevice_SetSoftDisplayFilter
// ******************************************************************
void WINAPI xbox::EMUPATCH(D3DDevice_SetSoftDisplayFilter)
(
bool_xt Enable
)
{
LOG_FUNC_ONE_ARG(Enable);
LOG_IGNORED();
}

View File

@ -40,7 +40,7 @@
void LookupTrampolinesD3D(); void LookupTrampolinesD3D();
// initialize render window // initialize render window
extern void CxbxInitWindow(bool bFullInit); extern void CxbxInitWindow();
void CxbxUpdateNativeD3DResources(); void CxbxUpdateNativeD3DResources();
@ -76,7 +76,7 @@ xbox::hresult_xt WINAPI EMUPATCH(Direct3D_CreateDevice)
X_D3DDevice **ppReturnedDeviceInterface X_D3DDevice **ppReturnedDeviceInterface
); );
xbox::hresult_xt WINAPI EMUPATCH(Direct3D_CreateDevice_16__LTCG_eax_BehaviorFlags_ebx_ppReturnedDeviceInterface) xbox::hresult_xt WINAPI EMUPATCH(Direct3D_CreateDevice_16__LTCG_eax4_ebx6)
( (
uint_xt Adapter, uint_xt Adapter,
D3DDEVTYPE DeviceType, D3DDEVTYPE DeviceType,
@ -84,7 +84,7 @@ xbox::hresult_xt WINAPI EMUPATCH(Direct3D_CreateDevice_16__LTCG_eax_BehaviorFlag
X_D3DPRESENT_PARAMETERS *pPresentationParameters X_D3DPRESENT_PARAMETERS *pPresentationParameters
); );
xbox::hresult_xt WINAPI EMUPATCH(Direct3D_CreateDevice_16__LTCG_eax_BehaviorFlags_ecx_ppReturnedDeviceInterface) xbox::hresult_xt WINAPI EMUPATCH(Direct3D_CreateDevice_16__LTCG_eax4_ecx6)
( (
uint_xt Adapter, uint_xt Adapter,
D3DDEVTYPE DeviceType, D3DDEVTYPE DeviceType,
@ -92,7 +92,7 @@ xbox::hresult_xt WINAPI EMUPATCH(Direct3D_CreateDevice_16__LTCG_eax_BehaviorFlag
X_D3DPRESENT_PARAMETERS *pPresentationParameters X_D3DPRESENT_PARAMETERS *pPresentationParameters
); );
xbox::hresult_xt WINAPI EMUPATCH(Direct3D_CreateDevice_4) xbox::hresult_xt WINAPI EMUPATCH(Direct3D_CreateDevice_4__LTCG_eax1_ecx3)
( (
X_D3DPRESENT_PARAMETERS *pPresentationParameters X_D3DPRESENT_PARAMETERS *pPresentationParameters
); );
@ -171,7 +171,7 @@ xbox::hresult_xt WINAPI EMUPATCH(D3DDevice_EndVisibilityTest)
dword_xt Index dword_xt Index
); );
xbox::hresult_xt WINAPI EMUPATCH(D3DDevice_EndVisibilityTest_0)(); xbox::hresult_xt WINAPI EMUPATCH(D3DDevice_EndVisibilityTest_0__LTCG_eax1)();
// ****************************************************************** // ******************************************************************
// * patch: D3DDevice_GetVisibilityTestResult // * patch: D3DDevice_GetVisibilityTestResult
@ -197,9 +197,9 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_LoadVertexShader)
dword_xt Address dword_xt Address
); );
xbox::void_xt WINAPI EMUPATCH(D3DDevice_LoadVertexShader_0__LTCG_eax_Address_ecx_Handle)(); xbox::void_xt WINAPI EMUPATCH(D3DDevice_LoadVertexShader_0__LTCG_ecx1_eax2)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_LoadVertexShader_0__LTCG_eax_Address_edx_Handle)(); xbox::void_xt WINAPI EMUPATCH(D3DDevice_LoadVertexShader_0__LTCG_edx1_eax2)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_LoadVertexShader_4) xbox::void_xt WINAPI EMUPATCH(D3DDevice_LoadVertexShader_4__LTCG_eax1)
( (
dword_xt Address dword_xt Address
); );
@ -309,9 +309,18 @@ X_D3DSurface* WINAPI EMUPATCH(D3DDevice_GetBackBuffer2_0__LTCG_eax1)();
// ****************************************************************** // ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_GetBackBuffer) xbox::void_xt WINAPI EMUPATCH(D3DDevice_GetBackBuffer)
( (
int_xt BackBuffer, int_xt BackBuffer,
D3DBACKBUFFER_TYPE Type, D3DBACKBUFFER_TYPE Type,
X_D3DSurface **ppBackBuffer X_D3DSurface **ppBackBuffer
);
// ******************************************************************
// * patch: D3DDevice_GetBackBuffer_8__LTCG_eax1
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_GetBackBuffer_8__LTCG_eax1)
(
D3DBACKBUFFER_TYPE Type,
X_D3DSurface **ppBackBuffer
); );
// ****************************************************************** // ******************************************************************
@ -428,7 +437,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetVertexShaderConstant)
dword_xt ConstantCount dword_xt ConstantCount
); );
xbox::void_xt __fastcall EMUPATCH(D3DDevice_SetVertexShaderConstant_8) xbox::void_xt __fastcall EMUPATCH(D3DDevice_SetVertexShaderConstant_8__LTCG_edx3)
( (
void*, void*,
dword_xt ConstantCount, dword_xt ConstantCount,
@ -474,6 +483,11 @@ xbox::void_xt __fastcall EMUPATCH(D3DDevice_SetVertexShaderConstantNotInline)
dword_xt ConstantCount dword_xt ConstantCount
); );
// ******************************************************************
// * patch: D3DDevice_SetVertexShaderConstantNotInline_0__LTCG_ebx1_edx2_eax3
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetVertexShaderConstantNotInline_0__LTCG_ebx1_edx2_eax3)();
// ****************************************************************** // ******************************************************************
// * patch: D3DDevice_SetVertexShaderConstantNotInlineFast // * patch: D3DDevice_SetVertexShaderConstantNotInlineFast
// ****************************************************************** // ******************************************************************
@ -509,7 +523,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader)
dword_xt Handle dword_xt Handle
); );
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader_0__LTCG_eax_handle)(); xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPixelShader_0__LTCG_eax1)();
// ****************************************************************** // ******************************************************************
// * patch: D3DDevice_CreateTexture2 // * patch: D3DDevice_CreateTexture2
@ -594,9 +608,9 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetIndices)
); );
// ****************************************************************** // ******************************************************************
// * patch: D3DDevice_SetIndices_4 // * patch: D3DDevice_SetIndices_4__LTCG_ebx1
// ****************************************************************** // ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetIndices_4) xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetIndices_4__LTCG_ebx1)
( (
uint_xt BaseVertexIndex uint_xt BaseVertexIndex
); );
@ -610,12 +624,12 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture)
X_D3DBaseTexture *pTexture X_D3DBaseTexture *pTexture
); );
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_pTexture) xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax2)
( (
dword_xt Stage dword_xt Stage
); );
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_Stage) xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax1)
( (
X_D3DBaseTexture *pTexture X_D3DBaseTexture *pTexture
); );
@ -679,9 +693,9 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetVertexData4f)
); );
// ****************************************************************** // ******************************************************************
// * patch: D3DDevice_SetVertexData4f_16 // * patch: D3DDevice_SetVertexData4f_16__LTCG_edi1
// ****************************************************************** // ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetVertexData4f_16) xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetVertexData4f_16__LTCG_edi1)
( (
float_xt a, float_xt a,
float_xt b, float_xt b,
@ -736,6 +750,14 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_RunPushBuffer)
X_D3DFixup *pFixup X_D3DFixup *pFixup
); );
// ******************************************************************
// * patch: D3DDevice_RunPushBuffer_4__LTCG_eax2
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_RunPushBuffer_4__LTCG_eax2)
(
X_D3DPushBuffer *pPushBuffer
);
// ****************************************************************** // ******************************************************************
// * patch: D3DDevice_Clear // * patch: D3DDevice_Clear
// ****************************************************************** // ******************************************************************
@ -768,7 +790,7 @@ dword_xt WINAPI EMUPATCH(D3DDevice_Swap)
dword_xt Flags dword_xt Flags
); );
dword_xt WINAPI EMUPATCH(D3DDevice_Swap_0)(); dword_xt WINAPI EMUPATCH(D3DDevice_Swap_0__LTCG_eax1)();
// ****************************************************************** // ******************************************************************
// * patch: IDirect3DResource8_Register // * patch: IDirect3DResource8_Register
@ -820,10 +842,21 @@ xbox::void_xt WINAPI EMUPATCH(Lock2DSurface)
( (
X_D3DPixelContainer *pPixelContainer, X_D3DPixelContainer *pPixelContainer,
D3DCUBEMAP_FACES FaceType, D3DCUBEMAP_FACES FaceType,
uint_xt Level, uint_xt Level,
D3DLOCKED_RECT *pLockedRect, D3DLOCKED_RECT *pLockedRect,
RECT *pRect, RECT *pRect,
dword_xt Flags dword_xt Flags
);
// ******************************************************************
// * patch: Lock2DSurface_16__LTCG_esi4_eax5
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(Lock2DSurface_16__LTCG_esi4_eax5)
(
X_D3DPixelContainer *pPixelContainer,
D3DCUBEMAP_FACES FaceType,
uint_xt Level,
dword_xt Flags
); );
// ****************************************************************** // ******************************************************************
@ -832,10 +865,21 @@ xbox::void_xt WINAPI EMUPATCH(Lock2DSurface)
xbox::void_xt WINAPI EMUPATCH(Lock3DSurface) xbox::void_xt WINAPI EMUPATCH(Lock3DSurface)
( (
X_D3DPixelContainer *pPixelContainer, X_D3DPixelContainer *pPixelContainer,
uint_xt Level, uint_xt Level,
D3DLOCKED_BOX *pLockedVolume, D3DLOCKED_BOX *pLockedVolume,
D3DBOX *pBox, D3DBOX *pBox,
dword_xt Flags dword_xt Flags
);
// ******************************************************************
// * patch: Lock3DSurface_16__LTCG_eax4
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(Lock3DSurface_16__LTCG_eax4)
(
X_D3DPixelContainer *pPixelContainer,
uint_xt Level,
D3DLOCKED_BOX *pLockedVolume,
dword_xt Flags
); );
#if 0 // patch disabled #if 0 // patch disabled
@ -964,6 +1008,11 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_EnableOverlay)
bool_xt Enable bool_xt Enable
); );
// ******************************************************************
// * patch: D3DDevice_EnableOverlay_0__LTCG
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_EnableOverlay_0__LTCG)();
// ****************************************************************** // ******************************************************************
// * patch: D3DDevice_UpdateOverlay // * patch: D3DDevice_UpdateOverlay
// ****************************************************************** // ******************************************************************
@ -972,7 +1021,18 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_UpdateOverlay)
X_D3DSurface *pSurface, X_D3DSurface *pSurface,
CONST RECT *SrcRect, CONST RECT *SrcRect,
CONST RECT *DstRect, CONST RECT *DstRect,
bool_xt EnableColorKey, bool_xt EnableColorKey,
D3DCOLOR ColorKey
);
// ******************************************************************
// * patch: D3DDevice_UpdateOverlay
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_UpdateOverlay_16__LTCG_eax2)
(
X_D3DSurface *pSurface,
CONST RECT *DstRect,
bool_xt EnableColorKey,
D3DCOLOR ColorKey D3DCOLOR ColorKey
); );
@ -1308,6 +1368,11 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_MultiplyTransform)
CONST D3DMATRIX *pMatrix CONST D3DMATRIX *pMatrix
); );
// ******************************************************************
// * patch: D3DDevice_MultiplyTransform_0__LTCG_ebx1_eax2
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_MultiplyTransform_0__LTCG_ebx1_eax2)();
// ****************************************************************** // ******************************************************************
// * patch: D3DDevice_GetTransform // * patch: D3DDevice_GetTransform
// ****************************************************************** // ******************************************************************
@ -1357,20 +1422,20 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetStreamSource)
uint_xt Stride uint_xt Stride
); );
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetStreamSource_0__LTCG_eax_StreamNumber_edi_pStreamData_ebx_Stride)(); xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetStreamSource_0__LTCG_eax1_edi2_ebx3)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetStreamSource_4) xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetStreamSource_4__LTCG_eax1_ebx2)
( (
uint_xt Stride uint_xt Stride
); );
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetStreamSource_8) xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetStreamSource_8__LTCG_eax1)
( (
X_D3DVertexBuffer *pStreamData, X_D3DVertexBuffer *pStreamData,
uint_xt Stride uint_xt Stride
); );
xbox::void_xt __fastcall EMUPATCH(D3DDevice_SetStreamSource_8__LTCG_edx_StreamNumber) xbox::void_xt __fastcall EMUPATCH(D3DDevice_SetStreamSource_8__LTCG_edx1)
( (
void*, void*,
uint_xt StreamNumber, uint_xt StreamNumber,
@ -1386,7 +1451,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetVertexShader)
dword_xt Handle dword_xt Handle
); );
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetVertexShader_0)(); xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetVertexShader_0__LTCG_ebx1)();
// ****************************************************************** // ******************************************************************
// * patch: D3DDevice_DrawVertices // * patch: D3DDevice_DrawVertices
@ -1491,6 +1556,14 @@ xbox::hresult_xt WINAPI EMUPATCH(D3DDevice_LightEnable)
bool_xt bEnable bool_xt bEnable
); );
// ******************************************************************
// * patch: D3DDevice_LightEnable_4__LTCG_eax1
// ******************************************************************
xbox::hresult_xt WINAPI EMUPATCH(D3DDevice_LightEnable_4__LTCG_eax1)
(
bool_xt bEnable
);
// ****************************************************************** // ******************************************************************
// * patch: D3DDevice_Release // * patch: D3DDevice_Release
// ****************************************************************** // ******************************************************************
@ -1523,9 +1596,9 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetRenderTarget)
); );
// ****************************************************************** // ******************************************************************
// * patch: D3DDevice_SetRenderTarget_0 // * patch: D3DDevice_SetRenderTarget_0__LTCG_ecx1_eax2
// ****************************************************************** // ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetRenderTarget_0)(); xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetRenderTarget_0__LTCG_ecx1_eax2)();
// ****************************************************************** // ******************************************************************
// * patch: D3D_CommonSetRenderTarget // * patch: D3D_CommonSetRenderTarget
@ -1546,7 +1619,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPalette)
X_D3DPalette *pPalette X_D3DPalette *pPalette
); );
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPalette_4) xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetPalette_4__LTCG_eax1)
( (
X_D3DPalette *pPalette X_D3DPalette *pPalette
); );
@ -1559,7 +1632,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetFlickerFilter)
dword_xt Filter dword_xt Filter
); );
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetFlickerFilter_0)(); xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetFlickerFilter_0__LTCG_esi1)();
// ****************************************************************** // ******************************************************************
// * patch: D3DDevice_SetSoftDisplayFilter // * patch: D3DDevice_SetSoftDisplayFilter
@ -1605,7 +1678,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_DeleteVertexShader)
dword_xt Handle dword_xt Handle
); );
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DeleteVertexShader_0)(); xbox::void_xt WINAPI EMUPATCH(D3DDevice_DeleteVertexShader_0__LTCG_eax1)();
// ****************************************************************** // ******************************************************************
// * patch: D3DDevice_GetShaderConstantMode // * patch: D3DDevice_GetShaderConstantMode
@ -1662,6 +1735,14 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_RunVertexStateShader)
CONST float_xt *pData CONST float_xt *pData
); );
// ******************************************************************
// * patch: D3DDevice_RunVertexStateShader_4__LTCG_esi2
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_RunVertexStateShader_4__LTCG_esi2)
(
dword_xt Address
);
// ****************************************************************** // ******************************************************************
// * patch: D3DDevice_LoadVertexShaderProgram // * patch: D3DDevice_LoadVertexShaderProgram
// ****************************************************************** // ******************************************************************
@ -2063,13 +2144,12 @@ void WINAPI EMUPATCH(D3D_SetCommonDebugRegisters)();
// ****************************************************************** // ******************************************************************
// * patch: D3D_BlockOnTime // * patch: D3D_BlockOnTime
// ****************************************************************** // ******************************************************************
void WINAPI EMUPATCH(D3D_BlockOnTime)( dword_xt Unknown1, int Unknown2 ); void WINAPI EMUPATCH(D3D_BlockOnTime)(dword_xt Time, int MakeSpace);
// ****************************************************************** // ******************************************************************
// * patch: D3D_BlockOnTime_4 // * patch: D3D_BlockOnTime_4__LTCG_eax1
// One of the parameters (unknown which) passed in EAX
// ****************************************************************** // ******************************************************************
void WINAPI EMUPATCH(D3D_BlockOnTime_4)( dword_xt Unknown1 ); void WINAPI EMUPATCH(D3D_BlockOnTime_4__LTCG_eax1)(int MakeSpace);
// ****************************************************************** // ******************************************************************
// * patch: D3D_BlockOnResource // * patch: D3D_BlockOnResource
@ -2082,9 +2162,9 @@ void WINAPI EMUPATCH(D3D_BlockOnResource)( X_D3DResource* pResource );
void WINAPI EMUPATCH(D3D_DestroyResource)( X_D3DResource* pResource ); void WINAPI EMUPATCH(D3D_DestroyResource)( X_D3DResource* pResource );
// ****************************************************************** // ******************************************************************
// * patch: D3D_DestroyResource__LTCG // * patch: D3D_DestroyResource_0__LTCG_edi1
// ****************************************************************** // ******************************************************************
void WINAPI EMUPATCH(D3D_DestroyResource__LTCG)(); void WINAPI EMUPATCH(D3D_DestroyResource_0__LTCG_edi1)();
// ****************************************************************** // ******************************************************************

View File

@ -52,6 +52,8 @@
#include <bitset> #include <bitset>
#include <filesystem> #include <filesystem>
#include "nv2a_vsh_emulator.h"
// External symbols : // External symbols :
extern xbox::X_STREAMINPUT g_Xbox_SetStreamSource[X_VSH_MAX_STREAMS]; // Declared in XbVertexBuffer.cpp extern xbox::X_STREAMINPUT g_Xbox_SetStreamSource[X_VSH_MAX_STREAMS]; // Declared in XbVertexBuffer.cpp
extern XboxRenderStateConverter XboxRenderStates; // Declared in Direct3D9.cpp extern XboxRenderStateConverter XboxRenderStates; // Declared in Direct3D9.cpp
@ -1625,3 +1627,44 @@ extern void EmuParseVshFunction
pCurToken += X_VSH_INSTRUCTION_SIZE; pCurToken += X_VSH_INSTRUCTION_SIZE;
} }
} }
void CxbxrImpl_RunVertexStateShader(DWORD Address, CONST FLOAT *pData)
{
// If pData is assigned, pData[0..3] is pushed towards nv2a transform data registers
// then sends the nv2a a command to launch the vertex shader function located at Address
if (Address >= NV2A_MAX_TRANSFORM_PROGRAM_LENGTH) {
LOG_TEST_CASE("Address out of bounds");
return;
}
NV2AState* dev = g_NV2A->GetDeviceState();
PGRAPHState* pg = &(dev->pgraph);
Nv2aVshProgram program = {}; // Note: This nulls program.steps
// TODO : Retain program globally and perform nv2a_vsh_parse_program only when
// the address-range we're about to emulate was modified since last parse.
// TODO : As a suggestion for this, parse all NV2A_MAX_TRANSFORM_PROGRAM_LENGTH slots,
// and here just point program.steps to global vsh_program_steps[Address].
Nv2aVshParseResult result = nv2a_vsh_parse_program(
&program, // Note : program.steps will be malloc'ed
GetCxbxVertexShaderSlotPtr(Address), // TODO : At some point, use pg->program_data[Address] here instead
NV2A_MAX_TRANSFORM_PROGRAM_LENGTH - Address);
if (result != NV2AVPR_SUCCESS) {
LOG_TEST_CASE("nv2a_vsh_parse_program failed");
// TODO : Dump Nv2aVshParseResult as string and program for debugging purposes
return;
}
Nv2aVshCPUXVSSExecutionState state_linkage;
Nv2aVshExecutionState state = nv2a_vsh_emu_initialize_xss_execution_state(
&state_linkage, (float*)pg->vsh_constants); // Note : This wil memset(state_linkage, 0)
if (pData != nullptr)
//if pData != nullptr, then it contains v0.xyzw, we shall copy the binary content directly.
memcpy(state_linkage.input_regs, pData, sizeof(state_linkage.input_regs));
nv2a_vsh_emu_execute_track_context_writes(&state, &program, pg->vsh_constants_dirty);
// Note: Above emulation's primary purpose is to update pg->vsh_constants and pg->vsh_constants_dirty
// therefor, nothing else needs to be done here, other than to cleanup
nv2a_vsh_program_destroy(&program); // Note: program.steps will be free'ed
}

View File

@ -252,5 +252,6 @@ extern void CxbxImpl_SelectVertexShader(DWORD Handle, DWORD Address);
extern void CxbxImpl_SetVertexShaderInput(DWORD Handle, UINT StreamCount, xbox::X_STREAMINPUT* pStreamInputs); extern void CxbxImpl_SetVertexShaderInput(DWORD Handle, UINT StreamCount, xbox::X_STREAMINPUT* pStreamInputs);
extern void CxbxImpl_SetVertexShaderConstant(INT Register, PVOID pConstantData, DWORD ConstantCount); extern void CxbxImpl_SetVertexShaderConstant(INT Register, PVOID pConstantData, DWORD ConstantCount);
extern void CxbxImpl_DeleteVertexShader(DWORD Handle); extern void CxbxImpl_DeleteVertexShader(DWORD Handle);
extern void CxbxrImpl_RunVertexStateShader(DWORD Address, CONST FLOAT* pData);
extern void CxbxVertexShaderSetFlags(); extern void CxbxVertexShaderSetFlags();
#endif #endif

View File

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

View File

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

View File

@ -208,8 +208,12 @@ void CDECL EmuRegisterSymbol(const char* library_str,
uint32_t library_flag, uint32_t library_flag,
uint32_t xref_index, uint32_t xref_index,
const char* symbol_str, const char* symbol_str,
uint32_t func_addr, xbaddr symbol_addr,
uint32_t revision) uint32_t build_version,
uint32_t symbol_type,
uint32_t call_type,
uint32_t param_count,
const XbSDBSymbolParam* param_list)
{ {
// Ignore registered symbol in current database. // Ignore registered symbol in current database.
uint32_t hasSymbol = g_SymbolAddresses[symbol_str]; uint32_t hasSymbol = g_SymbolAddresses[symbol_str];
@ -218,8 +222,8 @@ void CDECL EmuRegisterSymbol(const char* library_str,
// Output some details // Output some details
std::stringstream output; std::stringstream output;
output << "Symbol: 0x" << std::setfill('0') << std::setw(8) << std::hex << func_addr output << "Symbol: 0x" << std::setfill('0') << std::setw(8) << std::hex << symbol_addr
<< " -> " << symbol_str << " " << std::dec << revision; << " -> " << symbol_str << " " << std::dec << build_version;
#if 0 // TODO: XbSymbolDatabase - Need to create a structure for patch and stuff. #if 0 // TODO: XbSymbolDatabase - Need to create a structure for patch and stuff.
bool IsXRef = OovpaTable->Oovpa->XRefSaveIndex != XRefNoSaveIndex; bool IsXRef = OovpaTable->Oovpa->XRefSaveIndex != XRefNoSaveIndex;
@ -286,7 +290,7 @@ void CDECL EmuRegisterSymbol(const char* library_str,
output << "\n"; output << "\n";
g_SymbolAddresses[symbol_str] = func_addr; g_SymbolAddresses[symbol_str] = symbol_addr;
printf(output.str().c_str()); printf(output.str().c_str());
} }
@ -297,22 +301,22 @@ void EmuUpdateLLEStatus(uint32_t XbLibScan)
g_EmuShared->GetFlagsLLE(&FlagsLLE); g_EmuShared->GetFlagsLLE(&FlagsLLE);
if ((FlagsLLE & LLE_GPU) == false if ((FlagsLLE & LLE_GPU) == false
&& !((XbLibScan & XbSymbolLib_D3D8) > 0 && !((XbLibScan & XBSDBLIB_D3D8) > 0
|| (XbLibScan & XbSymbolLib_D3D8LTCG) > 0)) { || (XbLibScan & XBSDBLIB_D3D8LTCG) > 0)) {
bLLE_GPU = true; bLLE_GPU = true;
FlagsLLE ^= LLE_GPU; FlagsLLE ^= LLE_GPU;
EmuOutputMessage(XB_OUTPUT_MESSAGE_INFO, "Fallback to LLE GPU."); EmuOutputMessage(XB_OUTPUT_MESSAGE_INFO, "Fallback to LLE GPU.");
} }
if ((FlagsLLE & LLE_APU) == false if ((FlagsLLE & LLE_APU) == false
&& (XbLibScan & XbSymbolLib_DSOUND) == 0) { && (XbLibScan & XBSDBLIB_DSOUND) == 0) {
bLLE_APU = true; bLLE_APU = true;
FlagsLLE ^= LLE_APU; FlagsLLE ^= LLE_APU;
EmuOutputMessage(XB_OUTPUT_MESSAGE_INFO, "Fallback to LLE APU."); EmuOutputMessage(XB_OUTPUT_MESSAGE_INFO, "Fallback to LLE APU.");
} }
#if 0 // Reenable this when LLE USB actually works #if 0 // Reenable this when LLE USB actually works
if ((FlagsLLE & LLE_USB) == false if ((FlagsLLE & LLE_USB) == false
&& (XbLibScan & XbSymbolLib_XAPILIB) == 0) { && (XbLibScan & XBSDBLIB_XAPILIB) == 0) {
bLLE_USB = true; bLLE_USB = true;
FlagsLLE ^= LLE_USB; FlagsLLE ^= LLE_USB;
EmuOutputMessage(XB_OUTPUT_MESSAGE_INFO, "Fallback to LLE USB."); EmuOutputMessage(XB_OUTPUT_MESSAGE_INFO, "Fallback to LLE USB.");
@ -346,16 +350,16 @@ void EmuHLEIntercept(Xbe::Header *pXbeHeader)
if (xdkVersion < BuildVersion) { if (xdkVersion < BuildVersion) {
xdkVersion = BuildVersion; xdkVersion = BuildVersion;
} }
XbLibFlag = XbSymbolDatabase_LibraryToFlag(std::string(pLibraryVersion[v].szName, pLibraryVersion[v].szName + 8).c_str()); XbLibFlag = XbSDB_LibraryToFlag(std::string(pLibraryVersion[v].szName, pLibraryVersion[v].szName + 8).c_str());
XbLibScan |= XbLibFlag; XbLibScan |= XbLibFlag;
// Keep certain library versions for plugin usage. // Keep certain library versions for plugin usage.
if ((XbLibFlag & (XbSymbolLib_D3D8 | XbSymbolLib_D3D8LTCG)) > 0) { if ((XbLibFlag & (XBSDBLIB_D3D8 | XBSDBLIB_D3D8LTCG)) > 0) {
if (g_LibVersion_D3D8 < BuildVersion) { if (g_LibVersion_D3D8 < BuildVersion) {
g_LibVersion_D3D8 = BuildVersion; g_LibVersion_D3D8 = BuildVersion;
} }
} }
else if ((XbLibFlag & XbSymbolLib_DSOUND) > 0) { else if ((XbLibFlag & XBSDBLIB_DSOUND) > 0) {
g_LibVersion_DSOUND = BuildVersion; g_LibVersion_DSOUND = BuildVersion;
} }
} }
@ -363,8 +367,8 @@ void EmuHLEIntercept(Xbe::Header *pXbeHeader)
// Since XDK 4039 title does not have library version for DSOUND, let's check section header if it exists or not. // Since XDK 4039 title does not have library version for DSOUND, let's check section header if it exists or not.
for (unsigned int v = 0; v < pXbeHeader->dwSections; v++) { for (unsigned int v = 0; v < pXbeHeader->dwSections; v++) {
SectionName = (const char*)pSectionHeaders[v].dwSectionNameAddr; SectionName = (const char*)pSectionHeaders[v].dwSectionNameAddr;
if (strncmp(SectionName, Lib_DSOUND, 8) == 0) { if (strncmp(SectionName, LIB_DSOUND, 8) == 0) {
XbLibScan |= XbSymbolLib_DSOUND; XbLibScan |= XBSDBLIB_DSOUND;
// If DSOUND version is not set, we need to force set it. // If DSOUND version is not set, we need to force set it.
if (g_LibVersion_DSOUND == 0) { if (g_LibVersion_DSOUND == 0) {
@ -417,7 +421,7 @@ void EmuHLEIntercept(Xbe::Header *pXbeHeader)
// Verify the version of the cache file against the Symbol Database version hash // Verify the version of the cache file against the Symbol Database version hash
const uint32_t SymbolDatabaseVersionHash = symbolCacheData.GetLongValue(section_info, sect_info_keys.SymbolDatabaseVersionHash, /*Default=*/0); const uint32_t SymbolDatabaseVersionHash = symbolCacheData.GetLongValue(section_info, sect_info_keys.SymbolDatabaseVersionHash, /*Default=*/0);
if (SymbolDatabaseVersionHash == XbSymbolDatabase_LibraryVersion()) { if (SymbolDatabaseVersionHash == XbSDB_LibraryVersion()) {
g_SymbolCacheUsed = true; g_SymbolCacheUsed = true;
CSimpleIniA::TNamesDepend symbol_names; CSimpleIniA::TNamesDepend symbol_names;
@ -488,9 +492,9 @@ void EmuHLEIntercept(Xbe::Header *pXbeHeader)
} }
#endif #endif
XbSymbolDatabase_SetOutputMessage(EmuOutputMessage); XbSDB_SetOutputMessage(EmuOutputMessage);
XbSymbolScan(pXbeHeader, EmuRegisterSymbol, false); XbSDB_Scan(pXbeHeader, EmuRegisterSymbol, false);
} }
std::printf("\n"); std::printf("\n");
@ -499,7 +503,7 @@ void EmuHLEIntercept(Xbe::Header *pXbeHeader)
symbolCacheData.Reset(); symbolCacheData.Reset();
// Store Symbol Database version // Store Symbol Database version
symbolCacheData.SetLongValue(section_info, sect_info_keys.SymbolDatabaseVersionHash, XbSymbolDatabase_LibraryVersion(), nullptr, /*UseHex =*/false); symbolCacheData.SetLongValue(section_info, sect_info_keys.SymbolDatabaseVersionHash, XbSDB_LibraryVersion(), nullptr, /*UseHex =*/false);
// Store Certificate Details // Store Certificate Details
symbolCacheData.SetValue(section_certificate, sect_certificate_keys.Name, tAsciiTitle); symbolCacheData.SetValue(section_certificate, sect_certificate_keys.Name, tAsciiTitle);

658
src/core/hle/JVS/JVS.cpp Normal file
View File

@ -0,0 +1,658 @@
// 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 = (DWORD*)GetXboxSymbolPointer("JVS_g_pPINSA");
g_pPINSB = (DWORD*)GetXboxSymbolPointer("JVS_g_pPINSB");
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;
}
if (JvsSendCommandOffset2) {
JvsSendCommandVersion = 2;
}
if (JvsSendCommandOffset3) {
JvsSendCommandVersion = 3;
}
// Set state to a sane initial default
ChihiroBaseBoardState.Reset();
// Auto-Patch Chihiro Region Flag to match the desired game
uint8_t &region = (uint8_t &)g_BaseBoardQcFirmware[0x1F00];
auto regionFlags = g_MediaBoard->GetBootId().regionFlags;
// The region of the system can be converted to a game region flag by doing 1 << region
// This gives a bitmask that can be ANDed with the BootID region flags to check the games support
if ((regionFlags & (1 << region)) == 0) {
// The region was not compatible, so we need to patch the region flag
// This avoids "Error 05: This game is not acceptable by main board."
// We use USA,EXPORT,JAPAN to make sure mutiple-language games default to English first
if (regionFlags & MB_CHIHIRO_REGION_FLAG_USA) {
region = 2;
}
else if (regionFlags & MB_CHIHIRO_REGION_FLAG_EXPORT) {
region = 3;
}
else if (regionFlags & MB_CHIHIRO_REGION_FLAG_JAPAN) {
region = 1;
}
}
// Spawn the Chihiro/JVS Input Thread
std::thread(JvsInputThread).detach();
}
DWORD WINAPI xbox::EMUPATCH(JVS_SendCommand)
(
DWORD a1,
DWORD Command,
DWORD a3,
DWORD Length,
DWORD a5,
DWORD a6,
DWORD a7,
DWORD a8
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(a1)
LOG_FUNC_ARG(Command)
LOG_FUNC_ARG(a3)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(a5)
LOG_FUNC_ARG(a6)
LOG_FUNC_ARG(a7)
LOG_FUNC_ARG(a8)
LOG_FUNC_END;
LOG_UNIMPLEMENTED();
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsBACKUP_Read)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy((void*)Buffer, &g_BaseBoardBackupMemory[Offset], Length);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsBACKUP_Write)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy(&g_BaseBoardBackupMemory[Offset], (void*)Buffer, Length);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsEEPROM_Read)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG_OUT(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy((void*)Buffer, &g_BaseBoardEeprom[Offset], Length);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsEEPROM_Write)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG_OUT(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy(&g_BaseBoardEeprom[Offset], (void*)Buffer, Length);
std::error_code error;
g_BaseBoardEeprom.sync(error);
if (error) {
EmuLog(LOG_LEVEL::WARNING, "Couldn't sync EEPROM to disk");
}
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsFirmwareDownload)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG_OUT(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy((void*)Buffer, &g_BaseBoardQcFirmware[Offset], Length);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsFirmwareUpload)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy(&g_BaseBoardQcFirmware[Offset], (void*)Buffer, Length);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsNodeReceivePacket)
(
PUCHAR Buffer,
PDWORD Length,
DWORD a3
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG_OUT(Buffer)
LOG_FUNC_ARG_OUT(Length)
LOG_FUNC_ARG(a3)
LOG_FUNC_END
// Receive the packet from the connected IO board
uint8_t DeviceId = g_pJvsIo->GetDeviceId();
// TODO : "Number of packets received" below might imply multiple packets might need receiving here...
uint16_t payloadSize = (uint16_t)g_pJvsIo->ReceivePacket(&Buffer[6]);
if (payloadSize > 0) {
Buffer[0] = 0; // Empty header byte, ignored
Buffer[1] = 1; // Number of packets received
Buffer[2] = DeviceId;
Buffer[3] = 0; // Unused
*Length = payloadSize + 6;
// Write the payload size header field
*((uint16_t*)&Buffer[4]) = payloadSize; // Packet Length (bytes 4-5)
// TODO : Prevent little/big endian issues here by explicitly setting Buffer[4] and Buffer[5]
}
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsNodeSendPacket)
(
PUCHAR Buffer,
DWORD Length,
DWORD a3
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Buffer)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(a3)
LOG_FUNC_END
// Buffer contains two opening bytes, '00' and 'XX', where XX is the number of JVS packets to send
// Each JVS packet is prepended with a '00' byte, the rest of the packet is as-per the JVS I/O standard.
// Ignore Buffer[0] (should be 0x00)
unsigned packetCount = Buffer[1];
uint8_t* packetPtr = &Buffer[2]; // First JVS packet starts at offset 2;
for (unsigned i = 0; i < packetCount; i++) {
// Skip the separator byte (should be 0x00)
packetPtr++;
// Send the packet to the connected I/O board
size_t bytes = g_pJvsIo->SendPacket(packetPtr);
// Set packetPtr to the next packet
packetPtr += bytes;
}
RETURN(0);
}
// Binary Coded Decimal to Decimal conversion
uint8_t BcdToUint8(uint8_t value)
{
return value - 6 * (value >> 4);
}
uint8_t Uint8ToBcd(uint8_t value)
{
return value + 6 * (value / 10);
}
DWORD WINAPI xbox::EMUPATCH(JvsRTC_Read)
(
DWORD a1,
DWORD a2,
JvsRTCTime* pTime,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(a1)
LOG_FUNC_ARG(a2)
LOG_FUNC_ARG_OUT(time)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
time_t hostTime;
struct tm* hostTimeInfo;
time(&hostTime);
hostTimeInfo = localtime(&hostTime);
memset(pTime, 0, sizeof(JvsRTCTime));
pTime->day = Uint8ToBcd(hostTimeInfo->tm_mday);
pTime->month = Uint8ToBcd(hostTimeInfo->tm_mon + 1); // Chihiro month counter stats at 1
pTime->year = Uint8ToBcd(hostTimeInfo->tm_year - 100); // Chihiro starts counting from year 2000
pTime->hour = Uint8ToBcd(hostTimeInfo->tm_hour);
pTime->minute = Uint8ToBcd(hostTimeInfo->tm_min);
pTime->second = Uint8ToBcd(hostTimeInfo->tm_sec);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsRTC_Write)
(
DWORD a1,
DWORD a2,
JvsRTCTime* pTime,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(a1)
LOG_FUNC_ARG(a2)
LOG_FUNC_ARG_OUT(time)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
LOG_UNIMPLEMENTED();
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsScFirmwareDownload)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG_OUT(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy((void*)Buffer, &g_BaseBoardScFirmware[Offset], Length);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsScFirmwareUpload)
(
DWORD Offset,
DWORD Length,
PUCHAR Buffer,
DWORD a4
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Offset)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(Buffer)
LOG_FUNC_ARG(a4)
LOG_FUNC_END
memcpy(&g_BaseBoardScFirmware[Offset], (void*)Buffer, Length);
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsScReceiveMidi)
(
DWORD a1,
DWORD a2,
DWORD a3
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(a1)
LOG_FUNC_ARG(a2)
LOG_FUNC_ARG(a3)
LOG_FUNC_END
LOG_UNIMPLEMENTED();
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsScSendMidi)
(
DWORD a1,
DWORD a2,
DWORD a3
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(a1)
LOG_FUNC_ARG(a2)
LOG_FUNC_ARG(a3)
LOG_FUNC_END
LOG_UNIMPLEMENTED();
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsScReceiveRs323c)
(
PUCHAR Buffer,
DWORD Length,
DWORD a3
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Buffer)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(a3)
LOG_FUNC_END
LOG_UNIMPLEMENTED();
RETURN(0);
}
DWORD WINAPI xbox::EMUPATCH(JvsScSendRs323c)
(
PUCHAR Buffer,
DWORD Length,
DWORD a3
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(Buffer)
LOG_FUNC_ARG(Length)
LOG_FUNC_ARG(a3)
LOG_FUNC_END
LOG_UNIMPLEMENTED();
RETURN(0);
}

182
src/core/hle/JVS/JVS.h Normal file
View 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

View File

@ -29,6 +29,7 @@
#include "core\kernel\init\CxbxKrnl.h" #include "core\kernel\init\CxbxKrnl.h"
#include "core\kernel\support\Emu.h" #include "core\kernel\support\Emu.h"
#include "core\hle\D3D8\Direct3D9/Direct3D9.h" #include "core\hle\D3D8\Direct3D9/Direct3D9.h"
#include "core\hle\JVS\JVS.h"
#include "core\hle\DSOUND\DirectSound\DirectSound.hpp" #include "core\hle\DSOUND\DirectSound\DirectSound.hpp"
#include "Patches.hpp" #include "Patches.hpp"
#include "Intercept.hpp" #include "Intercept.hpp"
@ -66,12 +67,12 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
PATCH_ENTRY("D3DDevice_BeginPush_8", xbox::EMUPATCH(D3DDevice_BeginPush_8), 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_BeginVisibilityTest", xbox::EMUPATCH(D3DDevice_BeginVisibilityTest), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_BlockOnFence", xbox::EMUPATCH(D3DDevice_BlockOnFence), 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_Clear", xbox::EMUPATCH(D3DDevice_Clear), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_CopyRects", xbox::EMUPATCH(D3DDevice_CopyRects), 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), // PATCH_ENTRY("D3DDevice_CreateVertexShader", xbox::EMUPATCH(D3DDevice_CreateVertexShader), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DeleteVertexShader", xbox::EMUPATCH(D3DDevice_DeleteVertexShader), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_DeleteVertexShader", xbox::EMUPATCH(D3DDevice_DeleteVertexShader), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DeleteVertexShader_0", xbox::EMUPATCH(D3DDevice_DeleteVertexShader_0), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_DeleteVertexShader_0__LTCG_eax1", xbox::EMUPATCH(D3DDevice_DeleteVertexShader_0__LTCG_eax1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawIndexedVertices", xbox::EMUPATCH(D3DDevice_DrawIndexedVertices), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_DrawIndexedVertices", xbox::EMUPATCH(D3DDevice_DrawIndexedVertices), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawIndexedVerticesUP", xbox::EMUPATCH(D3DDevice_DrawIndexedVerticesUP), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_DrawIndexedVerticesUP", xbox::EMUPATCH(D3DDevice_DrawIndexedVerticesUP), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawRectPatch", xbox::EMUPATCH(D3DDevice_DrawRectPatch), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_DrawRectPatch", xbox::EMUPATCH(D3DDevice_DrawRectPatch), PATCH_HLE_D3D),
@ -82,12 +83,14 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
PATCH_ENTRY("D3DDevice_DrawVerticesUP", xbox::EMUPATCH(D3DDevice_DrawVerticesUP), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_DrawVerticesUP", xbox::EMUPATCH(D3DDevice_DrawVerticesUP), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawVerticesUP_12__LTCG_ebx3", xbox::EMUPATCH(D3DDevice_DrawVerticesUP_12__LTCG_ebx3), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_DrawVerticesUP_12__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_EnableOverlay", xbox::EMUPATCH(D3DDevice_EnableOverlay), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_EnableOverlay_0__LTCG", xbox::EMUPATCH(D3DDevice_EnableOverlay_0__LTCG), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_End", xbox::EMUPATCH(D3DDevice_End), 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), PATCH_ENTRY("D3DDevice_EndPush", xbox::EMUPATCH(D3DDevice_EndPush), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_EndVisibilityTest", xbox::EMUPATCH(D3DDevice_EndVisibilityTest), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_EndVisibilityTest", xbox::EMUPATCH(D3DDevice_EndVisibilityTest), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_EndVisibilityTest_0", xbox::EMUPATCH(D3DDevice_EndVisibilityTest_0), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_EndVisibilityTest_0__LTCG_eax1", xbox::EMUPATCH(D3DDevice_EndVisibilityTest_0__LTCG_eax1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_FlushVertexCache", xbox::EMUPATCH(D3DDevice_FlushVertexCache), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_FlushVertexCache", xbox::EMUPATCH(D3DDevice_FlushVertexCache), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_GetBackBuffer", xbox::EMUPATCH(D3DDevice_GetBackBuffer), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_GetBackBuffer", xbox::EMUPATCH(D3DDevice_GetBackBuffer), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_GetBackBuffer_8__LTCG_eax1", xbox::EMUPATCH(D3DDevice_GetBackBuffer_8__LTCG_eax1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_GetBackBuffer2", xbox::EMUPATCH(D3DDevice_GetBackBuffer2), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_GetBackBuffer2", xbox::EMUPATCH(D3DDevice_GetBackBuffer2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_GetBackBuffer2_0__LTCG_eax1", xbox::EMUPATCH(D3DDevice_GetBackBuffer2_0__LTCG_eax1), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_GetBackBuffer2_0__LTCG_eax1", xbox::EMUPATCH(D3DDevice_GetBackBuffer2_0__LTCG_eax1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_GetDisplayFieldStatus", xbox::EMUPATCH(D3DDevice_GetDisplayFieldStatus), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_GetDisplayFieldStatus", xbox::EMUPATCH(D3DDevice_GetDisplayFieldStatus), PATCH_HLE_D3D),
@ -106,18 +109,21 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
//PATCH_ENTRY("D3DDevice_GetVertexShaderSize", xbox::EMUPATCH(D3DDevice_GetVertexShaderSize), PATCH_HLE_D3D), //PATCH_ENTRY("D3DDevice_GetVertexShaderSize", xbox::EMUPATCH(D3DDevice_GetVertexShaderSize), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_GetVertexShaderType", xbox::EMUPATCH(D3DDevice_GetVertexShaderType), PATCH_HLE_D3D), //PATCH_ENTRY("D3DDevice_GetVertexShaderType", xbox::EMUPATCH(D3DDevice_GetVertexShaderType), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_GetViewportOffsetAndScale", xbox::EMUPATCH(D3DDevice_GetViewportOffsetAndScale), PATCH_HLE_D3D), //PATCH_ENTRY("D3DDevice_GetViewportOffsetAndScale", xbox::EMUPATCH(D3DDevice_GetViewportOffsetAndScale), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_GetViewportOffsetAndScale_0__LTCG_edx1_ecx2", xbox::EMUPATCH(D3DDevice_GetViewportOffsetAndScale_0__LTCG_edx1_ecx2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_GetVisibilityTestResult", xbox::EMUPATCH(D3DDevice_GetVisibilityTestResult), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_GetVisibilityTestResult", xbox::EMUPATCH(D3DDevice_GetVisibilityTestResult), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_InsertCallback", xbox::EMUPATCH(D3DDevice_InsertCallback), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_InsertCallback", xbox::EMUPATCH(D3DDevice_InsertCallback), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_InsertFence", xbox::EMUPATCH(D3DDevice_InsertFence), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_InsertFence", xbox::EMUPATCH(D3DDevice_InsertFence), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_IsBusy", xbox::EMUPATCH(D3DDevice_IsBusy), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_IsBusy", xbox::EMUPATCH(D3DDevice_IsBusy), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_IsFencePending", xbox::EMUPATCH(D3DDevice_IsFencePending), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_IsFencePending", xbox::EMUPATCH(D3DDevice_IsFencePending), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_LightEnable", xbox::EMUPATCH(D3DDevice_LightEnable), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_LightEnable", xbox::EMUPATCH(D3DDevice_LightEnable), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_LightEnable_4__LTCG_eax1", xbox::EMUPATCH(D3DDevice_LightEnable_4__LTCG_eax1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_LoadVertexShader", xbox::EMUPATCH(D3DDevice_LoadVertexShader), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_LoadVertexShader", xbox::EMUPATCH(D3DDevice_LoadVertexShader), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_LoadVertexShaderProgram", xbox::EMUPATCH(D3DDevice_LoadVertexShaderProgram), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_LoadVertexShaderProgram", xbox::EMUPATCH(D3DDevice_LoadVertexShaderProgram), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_LoadVertexShader_0__LTCG_eax_Address_ecx_Handle", xbox::EMUPATCH(D3DDevice_LoadVertexShader_0__LTCG_eax_Address_ecx_Handle), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_LoadVertexShader_0__LTCG_ecx1_eax2", xbox::EMUPATCH(D3DDevice_LoadVertexShader_0__LTCG_ecx1_eax2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_LoadVertexShader_0__LTCG_eax_Address_edx_Handle", xbox::EMUPATCH(D3DDevice_LoadVertexShader_0__LTCG_eax_Address_edx_Handle), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_LoadVertexShader_0__LTCG_edx1_eax2", xbox::EMUPATCH(D3DDevice_LoadVertexShader_0__LTCG_edx1_eax2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_LoadVertexShader_4", xbox::EMUPATCH(D3DDevice_LoadVertexShader_4), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_LoadVertexShader_4__LTCG_eax1", xbox::EMUPATCH(D3DDevice_LoadVertexShader_4__LTCG_eax1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_MultiplyTransform", xbox::EMUPATCH(D3DDevice_MultiplyTransform), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_MultiplyTransform", xbox::EMUPATCH(D3DDevice_MultiplyTransform), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_MultiplyTransform_0__LTCG_ebx1_eax2", xbox::EMUPATCH(D3DDevice_MultiplyTransform_0__LTCG_ebx1_eax2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_PersistDisplay", xbox::EMUPATCH(D3DDevice_PersistDisplay), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_PersistDisplay", xbox::EMUPATCH(D3DDevice_PersistDisplay), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_Present", xbox::EMUPATCH(D3DDevice_Present), PATCH_HLE_D3D), 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_PrimeVertexCache", xbox::EMUPATCH(D3DDevice_PrimeVertexCache), PATCH_HLE_D3D),
@ -125,84 +131,92 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
PATCH_ENTRY("D3DDevice_Reset_0__LTCG_edi1", xbox::EMUPATCH(D3DDevice_Reset_0__LTCG_edi1), 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_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_RunPushBuffer", xbox::EMUPATCH(D3DDevice_RunPushBuffer), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_RunPushBuffer_4__LTCG_eax2", xbox::EMUPATCH(D3DDevice_RunPushBuffer_4__LTCG_eax2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_RunVertexStateShader", xbox::EMUPATCH(D3DDevice_RunVertexStateShader), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_RunVertexStateShader", xbox::EMUPATCH(D3DDevice_RunVertexStateShader), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_RunVertexStateShader_4__LTCG_esi2", xbox::EMUPATCH(D3DDevice_RunVertexStateShader_4__LTCG_esi2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SelectVertexShader", xbox::EMUPATCH(D3DDevice_SelectVertexShader), 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_SelectVertexShaderDirect", xbox::EMUPATCH(D3DDevice_SelectVertexShaderDirect), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_SelectVertexShaderDirect_0__LTCG_eax1_ebx2", xbox::EMUPATCH(D3DDevice_SelectVertexShaderDirect_0__LTCG_eax1_ebx2), 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_0__LTCG_eax1_ebx2", xbox::EMUPATCH(D3DDevice_SelectVertexShader_0__LTCG_eax1_ebx2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SelectVertexShader_4__LTCG_eax1", xbox::EMUPATCH(D3DDevice_SelectVertexShader_4__LTCG_eax1), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SelectVertexShader_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_SetBackBufferScale", xbox::EMUPATCH(D3DDevice_SetBackBufferScale), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetDepthClipPlanes", xbox::EMUPATCH(D3DDevice_SetDepthClipPlanes), 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), //PATCH_ENTRY("D3DDevice_SetFlickerFilter", xbox::EMUPATCH(D3DDevice_SetFlickerFilter), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetFlickerFilter_0", xbox::EMUPATCH(D3DDevice_SetFlickerFilter_0), PATCH_HLE_D3D), //PATCH_ENTRY("D3DDevice_SetFlickerFilter_0__LTCG_esi1", xbox::EMUPATCH(D3DDevice_SetFlickerFilter_0__LTCG_esi1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetGammaRamp", xbox::EMUPATCH(D3DDevice_SetGammaRamp), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetGammaRamp", xbox::EMUPATCH(D3DDevice_SetGammaRamp), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetIndices", xbox::EMUPATCH(D3DDevice_SetIndices), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetIndices", xbox::EMUPATCH(D3DDevice_SetIndices), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetIndices_4", xbox::EMUPATCH(D3DDevice_SetIndices_4), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetIndices_4__LTCG_ebx1", xbox::EMUPATCH(D3DDevice_SetIndices_4__LTCG_ebx1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetLight", xbox::EMUPATCH(D3DDevice_SetLight), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetLight", xbox::EMUPATCH(D3DDevice_SetLight), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetMaterial", xbox::EMUPATCH(D3DDevice_SetMaterial), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetMaterial", xbox::EMUPATCH(D3DDevice_SetMaterial), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetModelView", xbox::EMUPATCH(D3DDevice_SetModelView), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetModelView", xbox::EMUPATCH(D3DDevice_SetModelView), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetPalette", xbox::EMUPATCH(D3DDevice_SetPalette), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetPalette", xbox::EMUPATCH(D3DDevice_SetPalette), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetPalette_4", xbox::EMUPATCH(D3DDevice_SetPalette_4), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetPalette_4__LTCG_eax1", xbox::EMUPATCH(D3DDevice_SetPalette_4__LTCG_eax1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetPixelShader", xbox::EMUPATCH(D3DDevice_SetPixelShader), 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_SetPixelShaderConstant", xbox::EMUPATCH(D3DDevice_SetPixelShaderConstant), 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_SetPixelShaderConstant_4__LTCG_ecx1_eax3", xbox::EMUPATCH(D3DDevice_SetPixelShaderConstant_4__LTCG_ecx1_eax3), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetPixelShader_0__LTCG_eax1", xbox::EMUPATCH(D3DDevice_SetPixelShader_0__LTCG_eax1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetRenderState_Simple", xbox::EMUPATCH(D3DDevice_SetRenderState_Simple), 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_SetRenderTarget", xbox::EMUPATCH(D3DDevice_SetRenderTarget), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetRenderTargetFast", xbox::EMUPATCH(D3DDevice_SetRenderTargetFast), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetRenderTargetFast", xbox::EMUPATCH(D3DDevice_SetRenderTargetFast), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetRenderTarget_0", xbox::EMUPATCH(D3DDevice_SetRenderTarget_0), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetRenderTarget_0__LTCG_ecx1_eax2", xbox::EMUPATCH(D3DDevice_SetRenderTarget_0__LTCG_ecx1_eax2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetScreenSpaceOffset", xbox::EMUPATCH(D3DDevice_SetScreenSpaceOffset), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetScreenSpaceOffset", xbox::EMUPATCH(D3DDevice_SetScreenSpaceOffset), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetShaderConstantMode", xbox::EMUPATCH(D3DDevice_SetShaderConstantMode), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetShaderConstantMode", xbox::EMUPATCH(D3DDevice_SetShaderConstantMode), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetShaderConstantMode_0__LTCG_eax1", xbox::EMUPATCH(D3DDevice_SetShaderConstantMode_0__LTCG_eax1), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetShaderConstantMode_0__LTCG_eax1", xbox::EMUPATCH(D3DDevice_SetShaderConstantMode_0__LTCG_eax1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetSoftDisplayFilter", xbox::EMUPATCH(D3DDevice_SetSoftDisplayFilter), PATCH_HLE_D3D), //PATCH_ENTRY("D3DDevice_SetSoftDisplayFilter", xbox::EMUPATCH(D3DDevice_SetSoftDisplayFilter), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetStipple", xbox::EMUPATCH(D3DDevice_SetStipple), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetStipple", xbox::EMUPATCH(D3DDevice_SetStipple), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetStreamSource", xbox::EMUPATCH(D3DDevice_SetStreamSource), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetStreamSource", xbox::EMUPATCH(D3DDevice_SetStreamSource), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetStreamSource_0__LTCG_eax_StreamNumber_edi_pStreamData_ebx_Stride", xbox::EMUPATCH(D3DDevice_SetStreamSource_0__LTCG_eax_StreamNumber_edi_pStreamData_ebx_Stride), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetStreamSource_0__LTCG_eax1_edi2_ebx3", xbox::EMUPATCH(D3DDevice_SetStreamSource_0__LTCG_eax1_edi2_ebx3), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetStreamSource_4", xbox::EMUPATCH(D3DDevice_SetStreamSource_4), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetStreamSource_4__LTCG_eax1_ebx2", xbox::EMUPATCH(D3DDevice_SetStreamSource_4__LTCG_eax1_ebx2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetStreamSource_8", xbox::EMUPATCH(D3DDevice_SetStreamSource_8), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetStreamSource_8__LTCG_eax1", xbox::EMUPATCH(D3DDevice_SetStreamSource_8__LTCG_eax1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetStreamSource_8__LTCG_edx_StreamNumber", xbox::EMUPATCH(D3DDevice_SetStreamSource_8__LTCG_edx_StreamNumber), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetStreamSource_8__LTCG_edx1", xbox::EMUPATCH(D3DDevice_SetStreamSource_8__LTCG_edx1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetSwapCallback", xbox::EMUPATCH(D3DDevice_SetSwapCallback), PATCH_HLE_D3D), 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", xbox::EMUPATCH(D3DDevice_SetTexture), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetTexture_4__LTCG_eax_pTexture", xbox::EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_pTexture), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetTexture_4__LTCG_eax2", xbox::EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetTexture_4__LTCG_eax_Stage", xbox::EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax_Stage), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetTexture_4__LTCG_eax1", xbox::EMUPATCH(D3DDevice_SetTexture_4__LTCG_eax1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetTransform", xbox::EMUPATCH(D3DDevice_SetTransform), 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_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), PATCH_ENTRY("D3DDevice_SetVertexData2f", xbox::EMUPATCH(D3DDevice_SetVertexData2f), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexData2s", xbox::EMUPATCH(D3DDevice_SetVertexData2s), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetVertexData2s", xbox::EMUPATCH(D3DDevice_SetVertexData2s), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexData4f", xbox::EMUPATCH(D3DDevice_SetVertexData4f), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetVertexData4f", xbox::EMUPATCH(D3DDevice_SetVertexData4f), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexData4f_16", xbox::EMUPATCH(D3DDevice_SetVertexData4f_16), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetVertexData4f_16__LTCG_edi1", xbox::EMUPATCH(D3DDevice_SetVertexData4f_16__LTCG_edi1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexData4s", xbox::EMUPATCH(D3DDevice_SetVertexData4s), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetVertexData4s", xbox::EMUPATCH(D3DDevice_SetVertexData4s), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexData4ub", xbox::EMUPATCH(D3DDevice_SetVertexData4ub), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetVertexData4ub", xbox::EMUPATCH(D3DDevice_SetVertexData4ub), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexDataColor", xbox::EMUPATCH(D3DDevice_SetVertexDataColor), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetVertexDataColor", xbox::EMUPATCH(D3DDevice_SetVertexDataColor), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexShader", xbox::EMUPATCH(D3DDevice_SetVertexShader), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetVertexShader", xbox::EMUPATCH(D3DDevice_SetVertexShader), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexShader_0", xbox::EMUPATCH(D3DDevice_SetVertexShader_0), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetVertexShader_0__LTCG_ebx1", xbox::EMUPATCH(D3DDevice_SetVertexShader_0__LTCG_ebx1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexShaderConstant", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstant), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetVertexShaderConstant", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstant), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexShaderConstant1", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstant1), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetVertexShaderConstant1", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstant1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexShaderConstant1Fast", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstant1Fast), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetVertexShaderConstant1Fast", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstant1Fast), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexShaderConstant4", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstant4), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetVertexShaderConstant4", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstant4), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexShaderConstantNotInline", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstantNotInline), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetVertexShaderConstantNotInline", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstantNotInline), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexShaderConstantNotInline_0__LTCG_ebx1_edx2_eax3", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstantNotInline_0__LTCG_ebx1_edx2_eax3), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexShaderConstantNotInlineFast", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstantNotInlineFast), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetVertexShaderConstantNotInlineFast", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstantNotInlineFast), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexShaderConstant_8", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstant_8), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SetVertexShaderConstant_8__LTCG_edx3", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstant_8__LTCG_edx3), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexShaderInput", xbox::EMUPATCH(D3DDevice_SetVertexShaderInput), 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_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_SetViewport", xbox::EMUPATCH(D3DDevice_SetViewport), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_Swap", xbox::EMUPATCH(D3DDevice_Swap), 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), PATCH_ENTRY("D3DDevice_Swap_0__LTCG_eax1", xbox::EMUPATCH(D3DDevice_Swap_0__LTCG_eax1), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SwitchTexture", xbox::EMUPATCH(D3DDevice_SwitchTexture), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_SwitchTexture", xbox::EMUPATCH(D3DDevice_SwitchTexture), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_UpdateOverlay", xbox::EMUPATCH(D3DDevice_UpdateOverlay), PATCH_HLE_D3D), PATCH_ENTRY("D3DDevice_UpdateOverlay", xbox::EMUPATCH(D3DDevice_UpdateOverlay), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_UpdateOverlay_16__LTCG_eax2", xbox::EMUPATCH(D3DDevice_UpdateOverlay_16__LTCG_eax2), PATCH_HLE_D3D),
PATCH_ENTRY("D3DResource_BlockUntilNotBusy", xbox::EMUPATCH(D3DResource_BlockUntilNotBusy), PATCH_HLE_D3D), PATCH_ENTRY("D3DResource_BlockUntilNotBusy", xbox::EMUPATCH(D3DResource_BlockUntilNotBusy), PATCH_HLE_D3D),
PATCH_ENTRY("D3D_BlockOnTime", xbox::EMUPATCH(D3D_BlockOnTime), PATCH_HLE_D3D), PATCH_ENTRY("D3D_BlockOnTime", xbox::EMUPATCH(D3D_BlockOnTime), PATCH_HLE_D3D),
PATCH_ENTRY("D3D_BlockOnTime_4", xbox::EMUPATCH(D3D_BlockOnTime_4), PATCH_HLE_D3D), PATCH_ENTRY("D3D_BlockOnTime_4__LTCG_eax1", xbox::EMUPATCH(D3D_BlockOnTime_4__LTCG_eax1), PATCH_HLE_D3D),
PATCH_ENTRY("D3D_CommonSetRenderTarget", xbox::EMUPATCH(D3D_CommonSetRenderTarget), PATCH_HLE_D3D), PATCH_ENTRY("D3D_CommonSetRenderTarget", xbox::EMUPATCH(D3D_CommonSetRenderTarget), PATCH_HLE_D3D),
PATCH_ENTRY("D3D_DestroyResource", xbox::EMUPATCH(D3D_DestroyResource), PATCH_HLE_D3D), PATCH_ENTRY("D3D_DestroyResource", xbox::EMUPATCH(D3D_DestroyResource), PATCH_HLE_D3D),
PATCH_ENTRY("D3D_DestroyResource__LTCG", xbox::EMUPATCH(D3D_DestroyResource__LTCG), PATCH_HLE_D3D), PATCH_ENTRY("D3D_DestroyResource_0__LTCG_edi1", xbox::EMUPATCH(D3D_DestroyResource_0__LTCG_edi1), PATCH_HLE_D3D),
PATCH_ENTRY("D3D_LazySetPointParams", xbox::EMUPATCH(D3D_LazySetPointParams), PATCH_HLE_D3D), PATCH_ENTRY("D3D_LazySetPointParams", xbox::EMUPATCH(D3D_LazySetPointParams), PATCH_HLE_D3D),
PATCH_ENTRY("D3D_SetCommonDebugRegisters", xbox::EMUPATCH(D3D_SetCommonDebugRegisters), PATCH_HLE_D3D), PATCH_ENTRY("D3D_SetCommonDebugRegisters", xbox::EMUPATCH(D3D_SetCommonDebugRegisters), PATCH_HLE_D3D),
PATCH_ENTRY("Direct3D_CreateDevice", xbox::EMUPATCH(Direct3D_CreateDevice), PATCH_HLE_D3D), PATCH_ENTRY("Direct3D_CreateDevice", xbox::EMUPATCH(Direct3D_CreateDevice), PATCH_HLE_D3D),
PATCH_ENTRY("Direct3D_CreateDevice_16__LTCG_eax_BehaviorFlags_ebx_ppReturnedDeviceInterface", xbox::EMUPATCH(Direct3D_CreateDevice_16__LTCG_eax_BehaviorFlags_ebx_ppReturnedDeviceInterface), PATCH_HLE_D3D), PATCH_ENTRY("Direct3D_CreateDevice_16__LTCG_eax4_ebx6", xbox::EMUPATCH(Direct3D_CreateDevice_16__LTCG_eax4_ebx6), PATCH_HLE_D3D),
PATCH_ENTRY("Direct3D_CreateDevice_16__LTCG_eax_BehaviorFlags_ecx_ppReturnedDeviceInterface", xbox::EMUPATCH(Direct3D_CreateDevice_16__LTCG_eax_BehaviorFlags_ecx_ppReturnedDeviceInterface), PATCH_HLE_D3D), PATCH_ENTRY("Direct3D_CreateDevice_16__LTCG_eax4_ecx6", xbox::EMUPATCH(Direct3D_CreateDevice_16__LTCG_eax4_ecx6), PATCH_HLE_D3D),
PATCH_ENTRY("Direct3D_CreateDevice_4", xbox::EMUPATCH(Direct3D_CreateDevice_4), PATCH_HLE_D3D), PATCH_ENTRY("Direct3D_CreateDevice_4__LTCG_eax1_ecx3", xbox::EMUPATCH(Direct3D_CreateDevice_4__LTCG_eax1_ecx3), PATCH_HLE_D3D),
PATCH_ENTRY("Lock2DSurface", xbox::EMUPATCH(Lock2DSurface), PATCH_HLE_D3D), PATCH_ENTRY("Lock2DSurface", xbox::EMUPATCH(Lock2DSurface), PATCH_HLE_D3D),
PATCH_ENTRY("Lock2DSurface_16__LTCG_esi4_eax5", xbox::EMUPATCH(Lock2DSurface_16__LTCG_esi4_eax5), PATCH_HLE_D3D),
PATCH_ENTRY("Lock3DSurface", xbox::EMUPATCH(Lock3DSurface), PATCH_HLE_D3D), PATCH_ENTRY("Lock3DSurface", xbox::EMUPATCH(Lock3DSurface), PATCH_HLE_D3D),
PATCH_ENTRY("Lock3DSurface_16__LTCG_eax4", xbox::EMUPATCH(Lock3DSurface_16__LTCG_eax4), PATCH_HLE_D3D),
// DSOUND // DSOUND
PATCH_ENTRY("CDirectSound3DCalculator_Calculate3D", xbox::EMUPATCH(CDirectSound3DCalculator_Calculate3D), PATCH_HLE_DSOUND), PATCH_ENTRY("CDirectSound3DCalculator_Calculate3D", xbox::EMUPATCH(CDirectSound3DCalculator_Calculate3D), PATCH_HLE_DSOUND),
@ -375,6 +389,54 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
//PATCH_ENTRY("timeSetEvent", xbox::EMUPATCH(timeSetEvent), PATCH_ALWAYS), //PATCH_ENTRY("timeSetEvent", xbox::EMUPATCH(timeSetEvent), PATCH_ALWAYS),
PATCH_ENTRY("XReadMUMetaData", xbox::EMUPATCH(XReadMUMetaData), PATCH_ALWAYS), PATCH_ENTRY("XReadMUMetaData", xbox::EMUPATCH(XReadMUMetaData), PATCH_ALWAYS),
PATCH_ENTRY("XUnmountMU", xbox::EMUPATCH(XUnmountMU), 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; std::unordered_map<std::string, subhook::Hook> g_FunctionHooks;

View File

@ -960,15 +960,41 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(SignalObjectAndWait)
NewTime.QuadPart += (static_cast<xbox::ulonglong_xt>(dwMilliseconds) * CLOCK_TIME_INCREMENT); 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); DWORD dwRet = SignalObjectAndWait(hObjectToSignal, hObjectToWaitOn, 0, bAlertable);
if (dwRet == WAIT_TIMEOUT) { if (dwRet == WAIT_TIMEOUT) {
return std::nullopt; return std::nullopt;
} }
return std::make_optional<DWORD>(dwRet); // 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
}, Timeout, bAlertable, UserMode); // 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);
} }
// ****************************************************************** // ******************************************************************

View File

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

View File

@ -98,6 +98,8 @@ typedef void* LPSECURITY_ATTRIBUTES;
#define X_STATUS_FILE_IS_A_DIRECTORY 0xC00000BAL #define X_STATUS_FILE_IS_A_DIRECTORY 0xC00000BAL
#define X_STATUS_END_OF_FILE 0xC0000011L #define X_STATUS_END_OF_FILE 0xC0000011L
#define X_STATUS_INVALID_PAGE_PROTECTION 0xC0000045L #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_CONFLICTING_ADDRESSES 0xC0000018L
#define X_STATUS_UNABLE_TO_FREE_VM 0xC000001AL #define X_STATUS_UNABLE_TO_FREE_VM 0xC000001AL
#define X_STATUS_FREE_VM_NOT_AT_BASE 0xC000009FL #define X_STATUS_FREE_VM_NOT_AT_BASE 0xC000009FL
@ -1945,7 +1947,7 @@ typedef struct _KTHREAD
/* 0x56/86 */ char_xt WaitNext; /* 0x56/86 */ char_xt WaitNext;
/* 0x57/87 */ char_xt WaitReason; /* 0x57/87 */ char_xt WaitReason;
/* 0x58/88 */ PKWAIT_BLOCK WaitBlockList; /* 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; /* 0x64/100 */ ulong_xt WaitTime;
/* 0x68/104 */ ulong_xt KernelApcDisable; /* 0x68/104 */ ulong_xt KernelApcDisable;
/* 0x6C/108 */ ulong_xt Quantum; /* 0x6C/108 */ ulong_xt Quantum;
@ -1969,6 +1971,8 @@ typedef struct _KTHREAD
} }
KTHREAD, *PKTHREAD, *RESTRICTED_POINTER PRKTHREAD; KTHREAD, *PKTHREAD, *RESTRICTED_POINTER PRKTHREAD;
#define X_MAXIMUM_SUSPEND_COUNT 0x7F
// ****************************************************************** // ******************************************************************
// * ETHREAD // * ETHREAD
// ****************************************************************** // ******************************************************************

View File

@ -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) //#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. // 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::boolean_xt RemoveEntryList(xbox::PLIST_ENTRY pEntry)
{ {
xbox::PLIST_ENTRY _EX_Flink = pEntry->Flink; xbox::PLIST_ENTRY _EX_Flink = pEntry->Flink;
@ -140,8 +142,6 @@ void RestoreInterruptMode(bool value)
g_bInterruptsEnabled = value; g_bInterruptsEnabled = value;
} }
extern void ExecuteDpcQueue();
void KiUnexpectedInterrupt() void KiUnexpectedInterrupt()
{ {
xbox::KeBugCheck(TRAP_CAUSE_UNKNOWN); // see xbox::KeBugCheck(TRAP_CAUSE_UNKNOWN); // see
@ -178,6 +178,33 @@ void CallSoftwareInterrupt(const xbox::KIRQL SoftwareIrql)
HalInterruptRequestRegister ^= (1 << 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 // This masks have been verified to be correct against a kernel dump
const DWORD IrqlMasks[] = { const DWORD IrqlMasks[] = {
0xFFFFFFFE, // IRQL 0 0xFFFFFFFE, // IRQL 0

View File

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

View File

@ -211,6 +211,7 @@ XBSYSAPI EXPORTNUM(2) xbox::void_xt NTAPI xbox::AvSendTVEncoderOption
LOG_UNIMPLEMENTED(); LOG_UNIMPLEMENTED();
break; break;
case AV_OPTION_FLICKER_FILTER: case AV_OPTION_FLICKER_FILTER:
// Test case: Is called from AvSetDisplayMode (kernel) and D3DDevice_SetFlickerFilter (D3D8) functions.
LOG_UNIMPLEMENTED(); LOG_UNIMPLEMENTED();
break; break;
case AV_OPTION_ZERO_MODE: case AV_OPTION_ZERO_MODE:
@ -220,6 +221,7 @@ XBSYSAPI EXPORTNUM(2) xbox::void_xt NTAPI xbox::AvSendTVEncoderOption
LOG_UNIMPLEMENTED(); LOG_UNIMPLEMENTED();
break; break;
case AV_OPTION_ENABLE_LUMA_FILTER: case AV_OPTION_ENABLE_LUMA_FILTER:
// Test case: Is called from AvSetDisplayMode (kernel) and D3DDevice_SetSoftDisplayFilter (D3D8) functions.
LOG_UNIMPLEMENTED(); LOG_UNIMPLEMENTED();
break; break;
case AV_OPTION_GUESS_FIELD: case AV_OPTION_GUESS_FIELD:

View File

@ -248,6 +248,14 @@ XBSYSAPI EXPORTNUM(66) xbox::ntstatus_xt NTAPI xbox::IoCreateFile
LOG_FUNC_ARG(Options) LOG_FUNC_ARG(Options)
LOG_FUNC_END; 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; NativeObjectAttributes nativeObjectAttributes;
// If we are NOT accessing a directory, and we match a partition path, we need to redirect to a partition.bin file // 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 // Force ShareAccess to all
ShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; 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)) if (SUCCEEDED(ret))
{ {
// redirect to NtCreateFile // redirect to NtCreateFile

View File

@ -97,10 +97,12 @@ namespace NtDll
typedef struct _DpcData { typedef struct _DpcData {
CRITICAL_SECTION Lock; CRITICAL_SECTION Lock;
std::atomic_flag IsDpcActive; std::atomic_flag IsDpcActive;
std::atomic_flag IsDpcPending;
xbox::LIST_ENTRY DpcQueue; // TODO : Use KeGetCurrentPrcb()->DpcListHead instead xbox::LIST_ENTRY DpcQueue; // TODO : Use KeGetCurrentPrcb()->DpcListHead instead
} DpcData; } DpcData;
DpcData g_DpcData = { 0 }; // Note : g_DpcData is initialized in InitDpcData() 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) xbox::ulonglong_xt LARGE_INTEGER2ULONGLONG(xbox::LARGE_INTEGER value)
{ {
@ -130,6 +132,33 @@ xbox::ulonglong_xt LARGE_INTEGER2ULONGLONG(xbox::LARGE_INTEGER value)
break; \ 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() // * EmuKeGetPcr()
@ -166,7 +195,7 @@ xbox::void_xt NTAPI xbox::KeSetSystemTime
) )
{ {
KIRQL OldIrql, OldIrql2; KIRQL OldIrql, OldIrql2;
LARGE_INTEGER DeltaTime, HostTime; LARGE_INTEGER DeltaTime;
PLIST_ENTRY ListHead, NextEntry; PLIST_ENTRY ListHead, NextEntry;
PKTIMER Timer; PKTIMER Timer;
LIST_ENTRY TempList, TempList2; LIST_ENTRY TempList, TempList2;
@ -184,10 +213,6 @@ xbox::void_xt NTAPI xbox::KeSetSystemTime
/* Query the system time now */ /* Query the system time now */
KeQuerySystemTime(OldTime); 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 */ /* Calculate the difference between the new and the old time */
DeltaTime.QuadPart = NewTime->QuadPart - OldTime->QuadPart; 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); KiTimerListExpire(&TempList2, OldIrql);
} }
@ -463,6 +488,7 @@ void ExecuteDpcQueue()
g_DpcData.IsDpcActive.test_and_set(); g_DpcData.IsDpcActive.test_and_set();
KeGetCurrentPrcb()->DpcRoutineActive = TRUE; // Experimental KeGetCurrentPrcb()->DpcRoutineActive = TRUE; // Experimental
LeaveCriticalSection(&(g_DpcData.Lock)); LeaveCriticalSection(&(g_DpcData.Lock));
EmuLog(LOG_LEVEL::DEBUG, "Global DpcQueue, calling DPC object 0x%.8X at 0x%.8X", pkdpc, pkdpc->DeferredRoutine); EmuLog(LOG_LEVEL::DEBUG, "Global DpcQueue, calling DPC object 0x%.8X at 0x%.8X", pkdpc, pkdpc->DeferredRoutine);
// Call the Deferred Procedure : // Call the Deferred Procedure :
@ -477,6 +503,8 @@ void ExecuteDpcQueue()
g_DpcData.IsDpcActive.clear(); g_DpcData.IsDpcActive.clear();
} }
g_DpcData.IsDpcPending.clear();
// Assert(g_DpcData._dwThreadId == GetCurrentThreadId()); // Assert(g_DpcData._dwThreadId == GetCurrentThreadId());
// Assert(g_DpcData._dwDpcThreadId == g_DpcData._dwThreadId); // Assert(g_DpcData._dwDpcThreadId == g_DpcData._dwThreadId);
// g_DpcData._dwDpcThreadId = 0; // g_DpcData._dwDpcThreadId = 0;
@ -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 // We can't remove NtDll::NtDelayExecution until all APCs queued by Io are implemented by our kernel as well
// Test case: Metal Slug 3 // 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; NtDll::LARGE_INTEGER ExpireTime;
ExpireTime.QuadPart = 0; ExpireTime.QuadPart = 0;
NTSTATUS Status = NtDll::NtDelayExecution(Alertable, &ExpireTime); 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) { if (Status >= 0 && Status != STATUS_ALERTED && Status != STATUS_USER_APC) {
return std::nullopt; return std::nullopt;
} }
return std::make_optional<ntstatus_xt>(Status); // 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
}, Interval, Alertable, WaitMode); // 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) { if (ret == X_STATUS_TIMEOUT) {
// NOTE: this function considers a timeout a success // 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->SystemArgument1 = SystemArgument1;
Apc->SystemArgument2 = SystemArgument2; Apc->SystemArgument2 = SystemArgument2;
if (Apc->Inserted) { boolean_xt result = KiInsertQueueApc(Apc, Increment);
KfLowerIrql(OldIrql); KfLowerIrql(OldIrql);
RETURN(FALSE); RETURN(result);
}
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);
}
} }
} }
@ -1266,18 +1277,25 @@ XBSYSAPI EXPORTNUM(119) xbox::boolean_xt NTAPI xbox::KeInsertQueueDpc
Dpc->SystemArgument1 = SystemArgument1; Dpc->SystemArgument1 = SystemArgument1;
Dpc->SystemArgument2 = SystemArgument2; Dpc->SystemArgument2 = SystemArgument2;
InsertTailList(&(g_DpcData.DpcQueue), &(Dpc->DpcListEntry)); 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 // TODO : Instead of DpcQueue, add the DPC to KeGetCurrentPrcb()->DpcListHead
// Signal the Dpc handling code there's work to do // Signal the Dpc handling code there's work to do
if (!IsDpcActive()) { if (!IsDpcActive()) {
HalRequestSoftwareInterrupt(DISPATCH_LEVEL); HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
} }
// OpenXbox has this instead: // OpenXbox has this instead:
// if (!pKPRCB->DpcRoutineActive && !pKPRCB->DpcInterruptRequested) { // if (!pKPRCB->DpcRoutineActive && !pKPRCB->DpcInterruptRequested) {
// pKPRCB->DpcInterruptRequested = TRUE; // pKPRCB->DpcInterruptRequested = TRUE;
} }
else {
LeaveCriticalSection(&(g_DpcData.Lock));
}
// Thread-safety is no longer required anymore // Thread-safety is no longer required anymore
LeaveCriticalSection(&(g_DpcData.Lock));
// TODO : Instead, enable interrupts - use KeLowerIrql(OldIrql) ? // TODO : Instead, enable interrupts - use KeLowerIrql(OldIrql) ?
RETURN(NeedsInsertion); RETURN(NeedsInsertion);
@ -1353,13 +1371,14 @@ XBSYSAPI EXPORTNUM(123) xbox::long_xt NTAPI xbox::KePulseEvent
} }
LONG OldState = Event->Header.SignalState; LONG OldState = Event->Header.SignalState;
KiWaitListLock();
if ((OldState == 0) && (IsListEmpty(&Event->Header.WaitListHead) == FALSE)) { if ((OldState == 0) && (IsListEmpty(&Event->Header.WaitListHead) == FALSE)) {
Event->Header.SignalState = 1; Event->Header.SignalState = 1;
// TODO: KiWaitTest(Event, Increment); KiWaitTest(Event, Increment);
// For now, we just sleep to give other threads time to wake std::this_thread::yield();
// KiWaitTest and related functions require correct thread scheduling to implement first }
// This will have to wait until CPU emulation at v1.0 else {
Sleep(1); KiWaitListUnlock();
} }
Event->Header.SignalState = 0; Event->Header.SignalState = 0;
@ -1385,9 +1404,7 @@ XBSYSAPI EXPORTNUM(124) xbox::long_xt NTAPI xbox::KeQueryBasePriorityThread
KIRQL OldIrql; KIRQL OldIrql;
KiLockDispatcherDatabase(&OldIrql); KiLockDispatcherDatabase(&OldIrql);
// It cannot fail because all thread handles are created by ob long_xt ret = Thread->Priority;
const auto& nativeHandle = GetNativeHandle<true>(reinterpret_cast<PETHREAD>(Thread)->UniqueThread);
long_xt ret = GetThreadPriority(*nativeHandle);
KiUnlockDispatcherDatabase(OldIrql); KiUnlockDispatcherDatabase(OldIrql);
@ -1540,12 +1557,14 @@ XBSYSAPI EXPORTNUM(132) xbox::long_xt NTAPI xbox::KeReleaseSemaphore
} }
Semaphore->Header.SignalState = adjusted_signalstate; Semaphore->Header.SignalState = adjusted_signalstate;
//TODO: Implement KiWaitTest KiWaitListLock();
#if 0
if ((initial_state == 0) && (IsListEmpty(&Semaphore->Header.WaitListHead) == FALSE)) { if ((initial_state == 0) && (IsListEmpty(&Semaphore->Header.WaitListHead) == FALSE)) {
KiWaitTest(&Semaphore->Header, Increment); KiWaitTest(&Semaphore->Header, Increment);
std::this_thread::yield();
}
else {
KiWaitListUnlock();
} }
#endif
if (Wait) { if (Wait) {
PKTHREAD current_thread = KeGetCurrentThread(); PKTHREAD current_thread = KeGetCurrentThread();
@ -1759,11 +1778,29 @@ XBSYSAPI EXPORTNUM(140) xbox::ulong_xt NTAPI xbox::KeResumeThread
{ {
LOG_FUNC_ONE_ARG(Thread); 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 XBSYSAPI EXPORTNUM(141) xbox::PLIST_ENTRY NTAPI xbox::KeRundownQueue
@ -1805,25 +1842,15 @@ XBSYSAPI EXPORTNUM(143) xbox::long_xt NTAPI xbox::KeSetBasePriorityThread
) )
{ {
LOG_FUNC_BEGIN LOG_FUNC_BEGIN
LOG_FUNC_ARG_OUT(Thread) LOG_FUNC_ARG(Thread)
LOG_FUNC_ARG_OUT(Priority) LOG_FUNC_ARG(Priority)
LOG_FUNC_END; LOG_FUNC_END;
KIRQL oldIRQL; KIRQL oldIRQL;
KiLockDispatcherDatabase(&oldIRQL); KiLockDispatcherDatabase(&oldIRQL);
// It cannot fail because all thread handles are created by ob Thread->Priority = Priority;
const auto &nativeHandle = GetNativeHandle<true>(reinterpret_cast<PETHREAD>(Thread)->UniqueThread); long_xt ret = Thread->Priority;
LONG ret = GetThreadPriority(*nativeHandle);
// This would work normally, but it will slow down the emulation,
// don't do that if the priority is higher then normal (so our own)!
if (Priority <= THREAD_PRIORITY_NORMAL) {
BOOL result = SetThreadPriority(*nativeHandle, Priority);
if (!result) {
EmuLog(LOG_LEVEL::WARNING, "SetThreadPriority failed: %s", WinError2Str().c_str());
}
}
KiUnlockDispatcherDatabase(oldIRQL); KiUnlockDispatcherDatabase(oldIRQL);
@ -1844,17 +1871,9 @@ XBSYSAPI EXPORTNUM(144) xbox::boolean_xt NTAPI xbox::KeSetDisableBoostThread
KIRQL oldIRQL; KIRQL oldIRQL;
KiLockDispatcherDatabase(&oldIRQL); KiLockDispatcherDatabase(&oldIRQL);
// It cannot fail because all thread handles are created by ob
const auto &nativeHandle = GetNativeHandle<true>(reinterpret_cast<PETHREAD>(Thread)->UniqueThread);
boolean_xt prevDisableBoost = Thread->DisableBoost; boolean_xt prevDisableBoost = Thread->DisableBoost;
Thread->DisableBoost = (CHAR)Disable; Thread->DisableBoost = (CHAR)Disable;
BOOL bRet = SetThreadPriorityBoost(*nativeHandle, Disable);
if (!bRet) {
EmuLog(LOG_LEVEL::WARNING, "SetThreadPriorityBoost failed: %s", WinError2Str().c_str());
}
KiUnlockDispatcherDatabase(oldIRQL); KiUnlockDispatcherDatabase(oldIRQL);
RETURN(prevDisableBoost); RETURN(prevDisableBoost);
@ -1889,7 +1908,9 @@ XBSYSAPI EXPORTNUM(145) xbox::long_xt NTAPI xbox::KeSetEvent
} }
LONG OldState = Event->Header.SignalState; LONG OldState = Event->Header.SignalState;
KiWaitListLock();
if (IsListEmpty(&Event->Header.WaitListHead) != FALSE) { if (IsListEmpty(&Event->Header.WaitListHead) != FALSE) {
KiWaitListUnlock();
Event->Header.SignalState = 1; Event->Header.SignalState = 1;
} else { } else {
PKWAIT_BLOCK WaitBlock = CONTAINING_RECORD(Event->Header.WaitListHead.Flink, KWAIT_BLOCK, WaitListEntry); PKWAIT_BLOCK WaitBlock = CONTAINING_RECORD(Event->Header.WaitListHead.Flink, KWAIT_BLOCK, WaitListEntry);
@ -1897,16 +1918,14 @@ XBSYSAPI EXPORTNUM(145) xbox::long_xt NTAPI xbox::KeSetEvent
(WaitBlock->WaitType != WaitAny)) { (WaitBlock->WaitType != WaitAny)) {
if (OldState == 0) { if (OldState == 0) {
Event->Header.SignalState = 1; Event->Header.SignalState = 1;
// TODO: KiWaitTest(Event, Increment); KiWaitTest(Event, Increment);
// For now, we just sleep to give other threads time to wake }
// See KePulseEvent else {
Sleep(1); KiWaitListUnlock();
} }
} else { } else {
// TODO: KiUnwaitThread(WaitBlock->Thread, (NTSTATUS)WaitBlock->WaitKey, Increment); KiUnwaitThread(WaitBlock->Thread, (NTSTATUS)WaitBlock->WaitKey, Increment);
// For now, we just sleep to give other threads time to wake KiWaitListUnlock();
// See KePulseEvent
Sleep(1);
} }
} }
@ -1943,6 +1962,7 @@ XBSYSAPI EXPORTNUM(146) xbox::void_xt NTAPI xbox::KeSetEventBoostPriority
return; return;
} }
KiWaitListLock();
if (IsListEmpty(&Event->Header.WaitListHead) != FALSE) { if (IsListEmpty(&Event->Header.WaitListHead) != FALSE) {
Event->Header.SignalState = 1; Event->Header.SignalState = 1;
} else { } else {
@ -1953,11 +1973,9 @@ XBSYSAPI EXPORTNUM(146) xbox::void_xt NTAPI xbox::KeSetEventBoostPriority
} }
WaitThread->Quantum = WaitThread->ApcState.Process->ThreadQuantum; WaitThread->Quantum = WaitThread->ApcState.Process->ThreadQuantum;
// TODO: KiUnwaitThread(WaitThread, X_STATUS_SUCCESS, 1); KiUnwaitThread(WaitThread, X_STATUS_SUCCESS, 1);
// For now, we just sleep to give other threads time to wake
// See KePulseEvent
Sleep(1);
} }
KiWaitListUnlock();
KiUnlockDispatcherDatabase(OldIrql); KiUnlockDispatcherDatabase(OldIrql);
} }
@ -1989,8 +2007,8 @@ XBSYSAPI EXPORTNUM(148) xbox::boolean_xt NTAPI xbox::KeSetPriorityThread
) )
{ {
LOG_FUNC_BEGIN LOG_FUNC_BEGIN
LOG_FUNC_ARG_OUT(Thread) LOG_FUNC_ARG(Thread)
LOG_FUNC_ARG_OUT(Priority) LOG_FUNC_ARG(Priority)
LOG_FUNC_END; LOG_FUNC_END;
LOG_UNIMPLEMENTED(); LOG_UNIMPLEMENTED();
@ -2103,11 +2121,38 @@ XBSYSAPI EXPORTNUM(152) xbox::ulong_xt NTAPI xbox::KeSuspendThread
{ {
LOG_FUNC_ONE_ARG(Thread); 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);
} }
// ****************************************************************** // ******************************************************************
@ -2203,15 +2248,13 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
// Wait Loop // Wait Loop
// This loop ends // This loop ends
PLARGE_INTEGER OriginalTime = Timeout; PLARGE_INTEGER OriginalTime = Timeout;
LARGE_INTEGER DueTime, NewTime; PKWAIT_BLOCK WaitBlock;
KWAIT_BLOCK StackWaitBlock;
PKWAIT_BLOCK WaitBlock = &StackWaitBlock;
BOOLEAN WaitSatisfied; BOOLEAN WaitSatisfied;
NTSTATUS WaitStatus; NTSTATUS WaitStatus;
PKMUTANT ObjectMutant; PKMUTANT ObjectMutant;
// Hack variable (remove this when the thread scheduler is here)
bool timeout_set = false;
do { do {
Thread->WaitBlockList = WaitBlockArray;
// Check if we need to let an APC run. This should immediately trigger APC interrupt via a call to UnlockDispatcherDatabase // 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)) { if (Thread->ApcState.KernelApcPending && (Thread->WaitIrql < APC_LEVEL)) {
KiUnlockDispatcherDatabase(Thread->WaitIrql); KiUnlockDispatcherDatabase(Thread->WaitIrql);
@ -2270,7 +2313,7 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
// Check if the wait can be satisfied immediately // Check if the wait can be satisfied immediately
if ((WaitType == WaitAll) && (WaitSatisfied)) { if ((WaitType == WaitAll) && (WaitSatisfied)) {
WaitBlock->NextWaitBlock = &WaitBlockArray[0]; WaitBlock->NextWaitBlock = &WaitBlockArray[0];
KiWaitSatisfyAll(WaitBlock); KiWaitSatisfyAllAndLock(WaitBlock);
WaitStatus = (NTSTATUS)Thread->WaitStatus; WaitStatus = (NTSTATUS)Thread->WaitStatus;
goto NoWait; goto NoWait;
} }
@ -2285,36 +2328,20 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
goto NoWait; goto NoWait;
} }
// Setup a timer for the thread but only once (for now) // Setup a timer for the thread
if (!timeout_set) { KiTimerLock();
KiTimerLock(); PKTIMER Timer = &Thread->Timer;
PKTIMER Timer = &Thread->Timer; PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock;
PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock; WaitBlock->NextWaitBlock = WaitTimer;
WaitBlock->NextWaitBlock = WaitTimer; Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry;
Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry; Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry;
Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry; WaitTimer->NextWaitBlock = WaitBlock;
WaitTimer->NextWaitBlock = WaitBlock; if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) { WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
KiTimerUnlock();
goto NoWait;
}
// Boring, ensure that we only set the thread timer once. Otherwise, this will cause to insert the same
// thread timer over and over in the timer list, which will prevent KiTimerExpiration from removing these
// duplicated timers and thus it will attempt to endlessly remove the same unremoved timers, causing a deadlock.
// This can be removed once KiSwapThread and the kernel/user APCs are implemented
timeout_set = true;
DueTime.QuadPart = Timer->DueTime.QuadPart;
KiTimerUnlock(); 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; goto NoWait;
} }
KiTimerUnlock();
} }
else { else {
WaitBlock->NextWaitBlock = WaitBlock; WaitBlock->NextWaitBlock = WaitBlock;
@ -2322,12 +2349,13 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
WaitBlock->NextWaitBlock = &WaitBlockArray[0]; WaitBlock->NextWaitBlock = &WaitBlockArray[0];
WaitBlock = &WaitBlockArray[0]; WaitBlock = &WaitBlockArray[0];
KiWaitListLock();
do { do {
ObjectMutant = (PKMUTANT)WaitBlock->Object; ObjectMutant = (PKMUTANT)WaitBlock->Object;
//InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry); InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
WaitBlock = WaitBlock->NextWaitBlock; WaitBlock = WaitBlock->NextWaitBlock;
} while (WaitBlock != &WaitBlockArray[0]); } while (WaitBlock != &WaitBlockArray[0]);
KiWaitListUnlock();
/* /*
TODO: We can't implement this and the return values until we have our own thread scheduler TODO: We can't implement this and the return values until we have our own thread scheduler
@ -2335,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 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 // If the current thread is processing a queue object, wake other treads using the same queue
PRKQUEUE Queue = (PRKQUEUE)Thread->Queue; PRKQUEUE Queue = (PRKQUEUE)Thread->Queue;
if (Queue != NULL) { if (Queue != NULL) {
@ -2349,7 +2374,7 @@ XBSYSAPI EXPORTNUM(158) xbox::ntstatus_xt NTAPI xbox::KeWaitForMultipleObjects
Thread->WaitMode = WaitMode; Thread->WaitMode = WaitMode;
Thread->WaitReason = (UCHAR)WaitReason; Thread->WaitReason = (UCHAR)WaitReason;
Thread->WaitTime = KeTickCount; Thread->WaitTime = KeTickCount;
//Thread->State = Waiting; Thread->State = Waiting;
//KiInsertWaitList(WaitMode, Thread); //KiInsertWaitList(WaitMode, Thread);
//WaitStatus = (NTSTATUS)KiSwapThread(); //WaitStatus = (NTSTATUS)KiSwapThread();
@ -2364,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 // 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 break;
if (Timeout != nullptr) {
Timeout = KiComputeWaitInterval(OriginalTime, &DueTime, &NewTime);
}
} }
// Raise IRQL to DISPATCH_LEVEL and lock the database (only if it's not already at this level) // Raise IRQL to DISPATCH_LEVEL and lock the database (only if it's not already at this level)
@ -2384,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 // 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 // So unlock the dispatcher database, lower the IRQ and return the status
Thread->State = Running;
KiUnlockDispatcherDatabase(Thread->WaitIrql); KiUnlockDispatcherDatabase(Thread->WaitIrql);
#if 0
// No need for this at the moment, since WaitApc already executes user APCs
if (WaitStatus == X_STATUS_USER_APC) { if (WaitStatus == X_STATUS_USER_APC) {
KiExecuteUserApc(); KiExecuteUserApc();
} }
#endif
RETURN(WaitStatus); RETURN(WaitStatus);
@ -2396,14 +2428,7 @@ NoWait:
// Unlock the database and return the status // Unlock the database and return the status
//TODO: KiAdjustQuantumThread(Thread); //TODO: KiAdjustQuantumThread(Thread);
// Don't forget to remove the thread timer if the objects were signaled before the timer expired Thread->State = Running;
// (remove this when the thread scheduler is here)
if (timeout_set && Thread->Timer.Header.Inserted == TRUE) {
KiTimerLock();
KxRemoveTreeTimer(&Thread->Timer);
KiTimerUnlock();
}
KiUnlockDispatcherDatabase(Thread->WaitIrql); KiUnlockDispatcherDatabase(Thread->WaitIrql);
if (WaitStatus == X_STATUS_USER_APC) { if (WaitStatus == X_STATUS_USER_APC) {
@ -2445,12 +2470,9 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
// Wait Loop // Wait Loop
// This loop ends // This loop ends
PLARGE_INTEGER OriginalTime = Timeout; PLARGE_INTEGER OriginalTime = Timeout;
LARGE_INTEGER DueTime, NewTime;
KWAIT_BLOCK StackWaitBlock; KWAIT_BLOCK StackWaitBlock;
PKWAIT_BLOCK WaitBlock = &StackWaitBlock; PKWAIT_BLOCK WaitBlock = &StackWaitBlock;
NTSTATUS WaitStatus; ntstatus_xt WaitStatus;
// Hack variable (remove this when the thread scheduler is here)
bool timeout_set = false;
do { do {
// Check if we need to let an APC run. This should immediately trigger APC interrupt via a call to UnlockDispatcherDatabase // 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)) { if (Thread->ApcState.KernelApcPending && (Thread->WaitIrql < APC_LEVEL)) {
@ -2498,36 +2520,20 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
goto NoWait; goto NoWait;
} }
// Setup a timer for the thread but only once (for now) // Setup a timer for the thread
if (!timeout_set) { KiTimerLock();
KiTimerLock(); PKTIMER Timer = &Thread->Timer;
PKTIMER Timer = &Thread->Timer; PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock;
PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock; WaitBlock->NextWaitBlock = WaitTimer;
WaitBlock->NextWaitBlock = WaitTimer; Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry;
Timer->Header.WaitListHead.Flink = &WaitTimer->WaitListEntry; Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry;
Timer->Header.WaitListHead.Blink = &WaitTimer->WaitListEntry; WaitTimer->NextWaitBlock = WaitBlock;
WaitTimer->NextWaitBlock = WaitBlock; if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) { WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
KiTimerUnlock();
goto NoWait;
}
// Boring, ensure that we only set the thread timer once. Otherwise, this will cause to insert the same
// thread timer over and over in the timer list, which will prevent KiTimerExpiration from removing these
// duplicated timers and thus it will attempt to endlessly remove the same unremoved timers, causing a deadlock.
// This can be removed once KiSwapThread and the kernel/user APCs are implemented
timeout_set = true;
DueTime.QuadPart = Timer->DueTime.QuadPart;
KiTimerUnlock(); 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; goto NoWait;
} }
KiTimerUnlock();
} }
else { else {
WaitBlock->NextWaitBlock = WaitBlock; WaitBlock->NextWaitBlock = WaitBlock;
@ -2539,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 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 // If the current thread is processing a queue object, wake other treads using the same queue
PRKQUEUE Queue = (PRKQUEUE)Thread->Queue; PRKQUEUE Queue = (PRKQUEUE)Thread->Queue;
if (Queue != NULL) { if (Queue != NULL) {
@ -2553,7 +2556,7 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
Thread->WaitMode = WaitMode; Thread->WaitMode = WaitMode;
Thread->WaitReason = (UCHAR)WaitReason; Thread->WaitReason = (UCHAR)WaitReason;
Thread->WaitTime = KeTickCount; Thread->WaitTime = KeTickCount;
// TODO: Thread->State = Waiting; Thread->State = Waiting;
//KiInsertWaitList(WaitMode, Thread); //KiInsertWaitList(WaitMode, Thread);
/* /*
@ -2568,13 +2571,21 @@ XBSYSAPI EXPORTNUM(159) xbox::ntstatus_xt NTAPI xbox::KeWaitForSingleObject
return WaitStatus; return WaitStatus;
} */ } */
// TODO: Remove this after we have our own scheduler and the above is implemented // Insert the WaitBlock
Sleep(0); KiWaitListLock();
InsertTailList(&ObjectMutant->Header.WaitListHead, &WaitBlock->WaitListEntry);
KiWaitListUnlock();
// Reduce the timout if necessary // TODO: Remove this after we have our own scheduler and the above is implemented
if (Timeout != nullptr) { WaitStatus = WaitApc<false>([](PKTHREAD Thread) -> std::optional<ntstatus_xt> {
Timeout = KiComputeWaitInterval(OriginalTime, &DueTime, &NewTime); 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 // Raise IRQL to DISPATCH_LEVEL and lock the database
@ -2589,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 // 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 // So unlock the dispatcher database, lower the IRQ and return the status
Thread->State = Running;
KiUnlockDispatcherDatabase(Thread->WaitIrql); KiUnlockDispatcherDatabase(Thread->WaitIrql);
#if 0
// No need for this at the moment, since WaitApc already executes user APCs
if (WaitStatus == X_STATUS_USER_APC) { if (WaitStatus == X_STATUS_USER_APC) {
KiExecuteUserApc(); KiExecuteUserApc();
} }
#endif
RETURN(WaitStatus); RETURN(WaitStatus);
@ -2601,14 +2616,7 @@ NoWait:
// Unlock the database and return the status // Unlock the database and return the status
//TODO: KiAdjustQuantumThread(Thread); //TODO: KiAdjustQuantumThread(Thread);
// Don't forget to remove the thread timer if the object was signaled before the timer expired Thread->State = Running;
// (remove this when the thread scheduler is here)
if (timeout_set && Thread->Timer.Header.Inserted == TRUE) {
KiTimerLock();
KxRemoveTreeTimer(&Thread->Timer);
KiTimerUnlock();
}
KiUnlockDispatcherDatabase(Thread->WaitIrql); KiUnlockDispatcherDatabase(Thread->WaitIrql);
if (WaitStatus == X_STATUS_USER_APC) { if (WaitStatus == X_STATUS_USER_APC) {

View File

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

View File

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

View File

@ -47,6 +47,12 @@ namespace xbox
int Acquired; int Acquired;
} KI_TIMER_LOCK; } 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 // NOTE: since the apc list is per-thread, we could also create a different mutex for each kthread
extern std::mutex KiApcListMtx; extern std::mutex KiApcListMtx;
@ -56,10 +62,11 @@ namespace xbox
void_xt KiTimerUnlock(); void_xt KiTimerUnlock();
void_xt KiClockIsr void_xt KiWaitListLock();
(
IN unsigned int ScalingFactor void_xt KiWaitListUnlock();
);
void_xt KiClockIsr(ulonglong_xt TotalUs);
xbox::void_xt NTAPI KiCheckTimerTable xbox::void_xt NTAPI KiCheckTimerTable
( (
@ -132,7 +139,7 @@ namespace xbox
IN KIRQL OldIrql IN KIRQL OldIrql
); );
void_xt FASTCALL KiWaitSatisfyAll void_xt KiWaitSatisfyAll
( (
IN PKWAIT_BLOCK WaitBlock IN PKWAIT_BLOCK WaitBlock
); );
@ -156,7 +163,8 @@ namespace xbox
); );
// Source: ReactOS // Source: ReactOS
void_xt NTAPI KiSuspendNop( void_xt NTAPI KiSuspendNop
(
IN PKAPC Apc, IN PKAPC Apc,
IN PKNORMAL_ROUTINE* NormalRoutine, IN PKNORMAL_ROUTINE* NormalRoutine,
IN PVOID* NormalContext, IN PVOID* NormalContext,
@ -164,6 +172,15 @@ namespace xbox
IN PVOID* SystemArgument2 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 // Source: ReactOS
void_xt NTAPI KiSuspendThread( void_xt NTAPI KiSuspendThread(
IN PVOID NormalContext, IN PVOID NormalContext,
@ -180,6 +197,48 @@ namespace xbox
IN PKSTART_ROUTINE StartRoutine, IN PKSTART_ROUTINE StartRoutine,
IN PVOID StartContext 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; extern xbox::KPROCESS KiUniqueProcess;

View File

@ -47,6 +47,7 @@ namespace NtDll
#include "core\kernel\support\EmuFile.h" // For EmuNtSymbolicLinkObject, NtStatusToString(), etc. #include "core\kernel\support\EmuFile.h" // For EmuNtSymbolicLinkObject, NtStatusToString(), etc.
#include "core\kernel\memory-manager\VMManager.h" // For g_VMManager #include "core\kernel\memory-manager\VMManager.h" // For g_VMManager
#include "core\kernel\support\NativeHandle.h" #include "core\kernel\support\NativeHandle.h"
#include "devices\Xbox.h"
#include "CxbxDebugger.h" #include "CxbxDebugger.h"
#pragma warning(disable:4005) // Ignore redefined status values #pragma warning(disable:4005) // Ignore redefined status values
@ -58,7 +59,7 @@ namespace NtDll
#include <mutex> #include <mutex>
// Prevent setting the system time from multiple threads at the same time // Prevent setting the system time from multiple threads at the same time
std::mutex NtSystemTimeMtx; xbox::RTL_CRITICAL_SECTION xbox::NtSystemTimeCritSec;
// ****************************************************************** // ******************************************************************
// * 0x00B8 - NtAllocateVirtualMemory() // * 0x00B8 - NtAllocateVirtualMemory()
@ -1039,7 +1040,7 @@ XBSYSAPI EXPORTNUM(206) xbox::ntstatus_xt NTAPI xbox::NtQueueApcThread
PKAPC Apc = static_cast<PKAPC>(ExAllocatePoolWithTag(sizeof(KAPC), 'pasP')); PKAPC Apc = static_cast<PKAPC>(ExAllocatePoolWithTag(sizeof(KAPC), 'pasP'));
if (Apc != zeroptr) { 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)) { if (!KeInsertQueueApc(Apc, ApcStatusBlock, ApcReserved, 0)) {
ExFreePool(Apc); ExFreePool(Apc);
result = X_STATUS_UNSUCCESSFUL; result = X_STATUS_UNSUCCESSFUL;
@ -1711,6 +1712,16 @@ XBSYSAPI EXPORTNUM(219) xbox::ntstatus_xt NTAPI xbox::NtReadFile
CxbxDebugger::ReportFileRead(FileHandle, Length, Offset); 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) { if (ApcRoutine != nullptr) {
// Pack the original parameters to a wrapped context for a custom APC routine // Pack the original parameters to a wrapped context for a custom APC routine
CxbxIoDispatcherContext* cxbxContext = new CxbxIoDispatcherContext(IoStatusBlock, ApcRoutine, ApcContext); 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_ARG_OUT(PreviousSuspendCount)
LOG_FUNC_END; LOG_FUNC_END;
if (const auto &nativeHandle = GetNativeHandle(ThreadHandle)) { PETHREAD Thread;
// Thread handles are created by ob ntstatus_xt result = ObReferenceObjectByHandle(ThreadHandle, &PsThreadObjectType, reinterpret_cast<PVOID *>(&Thread));
RETURN(NtDll::NtResumeThread(*nativeHandle, (::PULONG)PreviousSuspendCount)); if (!X_NT_SUCCESS(result)) {
} RETURN(result);
else {
RETURN(X_STATUS_INVALID_HANDLE);
} }
// 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; ret = STATUS_ACCESS_VIOLATION;
} }
else { else {
NtSystemTimeMtx.lock(); RtlEnterCriticalSectionAndRegion(&NtSystemTimeCritSec);
NewSystemTime = *SystemTime; NewSystemTime = *SystemTime;
if (NewSystemTime.u.HighPart > 0 && NewSystemTime.u.HighPart <= 0x20000000) { if (NewSystemTime.u.HighPart > 0 && NewSystemTime.u.HighPart <= 0x20000000) {
/* Convert the time and set it in HAL */ /* Convert the time and set it in HAL */
@ -1991,7 +2007,7 @@ XBSYSAPI EXPORTNUM(228) xbox::ntstatus_xt NTAPI xbox::NtSetSystemTime
else { else {
ret = STATUS_INVALID_PARAMETER; ret = STATUS_INVALID_PARAMETER;
} }
NtSystemTimeMtx.unlock(); RtlLeaveCriticalSectionAndRegion(&NtSystemTimeCritSec);
} }
RETURN(ret); RETURN(ret);
@ -2079,15 +2095,30 @@ XBSYSAPI EXPORTNUM(231) xbox::ntstatus_xt NTAPI xbox::NtSuspendThread
LOG_FUNC_ARG_OUT(PreviousSuspendCount) LOG_FUNC_ARG_OUT(PreviousSuspendCount)
LOG_FUNC_END; LOG_FUNC_END;
if (const auto &nativeHandle = GetNativeHandle(ThreadHandle)) { PETHREAD Thread;
// Thread handles are created by ob ntstatus_xt result = ObReferenceObjectByHandle(ThreadHandle, &PsThreadObjectType, reinterpret_cast<PVOID *>(&Thread));
RETURN(NtDll::NtSuspendThread(*nativeHandle, (::PULONG)PreviousSuspendCount)); if (!X_NT_SUCCESS(result)) {
} RETURN(result);
else {
RETURN(X_STATUS_INVALID_HANDLE);
} }
// 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])) { if (const auto &nativeHandle = GetNativeHandle(Handles[i])) {
// This is a ob handle, so replace it with its native counterpart // This is a ob handle, so replace it with its native counterpart
nativeHandles[i] = *nativeHandle; nativeHandles[i] = *nativeHandle;
EmuLog(LOG_LEVEL::DEBUG, "xbox handle: %p", nativeHandles[i]);
} }
else { else {
nativeHandles[i] = Handles[i]; 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 // 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; NtDll::LARGE_INTEGER ExpireTime;
ExpireTime.QuadPart = 0; ExpireTime.QuadPart = 0;
NTSTATUS Status = NtDll::NtWaitForMultipleObjects( NTSTATUS Status = NtDll::NtWaitForMultipleObjects(
@ -2221,8 +2260,11 @@ XBSYSAPI EXPORTNUM(235) xbox::ntstatus_xt NTAPI xbox::NtWaitForMultipleObjectsEx
if (Status == STATUS_TIMEOUT) { if (Status == STATUS_TIMEOUT) {
return std::nullopt; return std::nullopt;
} }
return std::make_optional<ntstatus_xt>(Status); // 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
}, Timeout, Alertable, WaitMode); // 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); RETURN(ret);
} }
@ -2269,6 +2311,16 @@ XBSYSAPI EXPORTNUM(236) xbox::ntstatus_xt NTAPI xbox::NtWriteFile
CxbxDebugger::ReportFileWrite(FileHandle, Length, Offset); 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) { if (ApcRoutine != nullptr) {
// Pack the original parameters to a wrapped context for a custom APC routine // Pack the original parameters to a wrapped context for a custom APC routine
CxbxIoDispatcherContext* cxbxContext = new CxbxIoDispatcherContext(IoStatusBlock, ApcRoutine, ApcContext); CxbxIoDispatcherContext* cxbxContext = new CxbxIoDispatcherContext(IoStatusBlock, ApcRoutine, ApcContext);

View File

@ -121,6 +121,8 @@ static unsigned int WINAPI PCSTProxy
params.Ethread, params.Ethread,
params.TlsDataSize); params.TlsDataSize);
xbox::KiExecuteKernelApc();
auto routine = (xbox::PKSYSTEM_ROUTINE)StartFrame->SystemRoutine; auto routine = (xbox::PKSYSTEM_ROUTINE)StartFrame->SystemRoutine;
// Debugging notice : When the below line shows up with an Exception dialog and a // 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 // 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); assert(dupHandle);
RegisterXboxHandle(eThread->UniqueThread, dupHandle); RegisterXboxHandle(eThread->UniqueThread, dupHandle);
eThread->Tcb.Priority = GetThreadPriority(handle);
g_AffinityPolicy->SetAffinityXbox(handle); g_AffinityPolicy->SetAffinityXbox(handle);
// Now that ThreadId is populated and affinity is changed, resume the thread (unless the guest passed CREATE_SUSPENDED) // Wait for the initialization of the remaining thread state
if (!CreateSuspended) { KeSuspendThreadEx(&eThread->Tcb);
ResumeThread(handle); 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 : // 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]", 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); *ThreadHandle, eThread->UniqueThread, handle, ThreadId);
@ -493,10 +506,13 @@ XBSYSAPI EXPORTNUM(258) xbox::void_xt NTAPI xbox::PsTerminateSystemThread
KeQuerySystemTime(&eThread->ExitTime); KeQuerySystemTime(&eThread->ExitTime);
eThread->ExitStatus = ExitStatus; eThread->ExitStatus = ExitStatus;
eThread->Tcb.Header.SignalState = 1; eThread->Tcb.Header.SignalState = 1;
KiWaitListLock();
if (!IsListEmpty(&eThread->Tcb.Header.WaitListHead)) { if (!IsListEmpty(&eThread->Tcb.Header.WaitListHead)) {
// TODO: Implement KiWaitTest's relative objects usage KiWaitTest((PVOID)&eThread->Tcb, 0);
//KiWaitTest() std::this_thread::yield();
assert(0); }
else {
KiWaitListUnlock();
} }
if (GetNativeHandle(eThread->UniqueThread)) { if (GetNativeHandle(eThread->UniqueThread)) {

View File

@ -89,6 +89,11 @@ xbox::boolean_xt RtlpCaptureStackLimits(
return TRUE; return TRUE;
} }
xbox::void_xt xbox::RtlInitSystem()
{
xbox::RtlInitializeCriticalSection(&NtSystemTimeCritSec);
}
// ****************************************************************** // ******************************************************************
// * 0x0104 - RtlAnsiStringToUnicodeString() // * 0x0104 - RtlAnsiStringToUnicodeString()
// ****************************************************************** // ******************************************************************

View File

@ -99,9 +99,6 @@ bool g_bIsChihiro = false;
bool g_bIsDevKit = false; bool g_bIsDevKit = false;
bool g_bIsRetail = 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 // Set by the VMManager during initialization. Exported because it's needed in other parts of the emu
size_t g_SystemMaxMemory = 0; 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 // Define function located in EmuXApi so we can call it from here
void SetupXboxDeviceTypes(); void SetupXboxDeviceTypes();
extern xbox::void_xt NTAPI system_events(xbox::PVOID arg);
void SetupPerTitleKeys() void SetupPerTitleKeys()
{ {
// Generate per-title keys from the XBE Certificate // Generate per-title keys from the XBE Certificate
@ -329,64 +328,6 @@ void InitSoftwareInterrupts()
} }
#endif #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) void MapThunkTable(uint32_t* kt, uint32_t* pThunkTable)
{ {
const bool SendDebugReports = (pThunkTable == CxbxKrnl_KernelThunkTable) && CxbxDebugger::CanReport(); const bool SendDebugReports = (pThunkTable == CxbxKrnl_KernelThunkTable) && CxbxDebugger::CanReport();
@ -573,12 +514,84 @@ static bool CxbxrKrnlXbeSystemSelector(int BootFlags,
// Load Xbe (this one will reside above WinMain's virtual_memory_placeholder) // Load Xbe (this one will reside above WinMain's virtual_memory_placeholder)
std::filesystem::path xbeDirectory = std::filesystem::path(xbePath).parent_path(); std::filesystem::path xbeDirectory = std::filesystem::path(xbePath).parent_path();
CxbxKrnl_Xbe = new Xbe(xbePath.c_str()); // TODO : Instead of using the Xbe class, port Dxbx _ReadXbeBlock()
if (CxbxKrnl_Xbe->HasFatalError()) {
CxbxrAbort(CxbxKrnl_Xbe->GetError().c_str());
return false;
}
// Check the signature of the xbe
if (CxbxKrnl_Xbe->CheckSignature()) {
EmuLogInit(LOG_LEVEL::INFO, "Valid xbe signature. Xbe is legit");
}
else {
EmuLogInit(LOG_LEVEL::WARNING, "Invalid xbe signature. Homebrew, tampered or pirated xbe?");
}
// Check the integrity of the xbe sections
for (uint32_t sectionIndex = 0; sectionIndex < CxbxKrnl_Xbe->m_Header.dwSections; sectionIndex++) {
if (CxbxKrnl_Xbe->CheckSectionIntegrity(sectionIndex)) {
EmuLogInit(LOG_LEVEL::INFO, "SHA hash check of section %s successful", CxbxKrnl_Xbe->m_szSectionName[sectionIndex]);
}
else {
EmuLogInit(LOG_LEVEL::WARNING, "SHA hash of section %s doesn't match, section is corrupted", CxbxKrnl_Xbe->m_szSectionName[sectionIndex]);
}
}
// If CLI has given console type, then enforce it.
if (cli_config::hasKey(cli_config::system_chihiro)) {
EmuLogInit(LOG_LEVEL::INFO, "Auto detect is disabled, running as chihiro.");
emulate_system = SYSTEM_CHIHIRO;
}
else if (cli_config::hasKey(cli_config::system_devkit)) {
EmuLogInit(LOG_LEVEL::INFO, "Auto detect is disabled, running as devkit.");
emulate_system = SYSTEM_DEVKIT;
}
else if (cli_config::hasKey(cli_config::system_retail)) {
EmuLogInit(LOG_LEVEL::INFO, "Auto detect is disabled, running as retail.");
emulate_system = SYSTEM_XBOX;
}
// Otherwise, use auto detect method.
else {
// 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:
emulate_system = SYSTEM_CHIHIRO;
break;
case XbeType::xtDebug:
emulate_system = SYSTEM_DEVKIT;
break;
case XbeType::xtRetail:
emulate_system = SYSTEM_XBOX;
break;
DEFAULT_UNREACHABLE;
}
// If the XBE path contains a boot.id, it must be a Chihiro title
// This is necessary as some Chihiro games use the Debug xor instead of the Chihiro ones
// which means we cannot rely on that alone.
if (std::filesystem::exists(xbeDirectory / "boot.id")) {
emulate_system = SYSTEM_CHIHIRO;
}
}
EmuLogInit(LOG_LEVEL::INFO, "Host's compatible system types: %2X", reserved_systems);
// If the system to emulate isn't supported on host, enforce failure.
if (!isSystemFlagSupport(reserved_systems, emulate_system)) {
CxbxrAbort("Unable to emulate system type due to host is not able to reserve required memory ranges.");
return false;
}
// Clear emulation system from reserved systems so all unneeded memory ranges can be freed.
reserved_systems &= ~emulate_system;
#ifdef CHIHIRO_WORK #ifdef CHIHIRO_WORK
// If the Xbe is Chihiro, and we were not launched by SEGABOOT, we need to load SEGABOOT from the Chihiro Media Board rom instead! // If the Xbe is Chihiro, and we were not launched by SEGABOOT, we need to load SEGABOOT from the Chihiro Media Board rom instead!
// If the XBE path contains a boot.id, it must be a Chihiro title if (BootFlags == BOOT_NONE && emulate_system == SYSTEM_CHIHIRO) {
// This is necessary as some Chihiro games use the Debug xor instead of the Chihiro ones
// which means we cannot rely on that alone.
if (BootFlags == BOOT_NONE && std::filesystem::exists(xbeDirectory / "boot.id")) {
std::string chihiroMediaBoardRom = g_DataFilePath + "/EmuDisk/" + MediaBoardRomFile; std::string chihiroMediaBoardRom = g_DataFilePath + "/EmuDisk/" + MediaBoardRomFile;
if (!std::filesystem::exists(chihiroMediaBoardRom)) { if (!std::filesystem::exists(chihiroMediaBoardRom)) {
@ -639,77 +652,9 @@ static bool CxbxrKrnlXbeSystemSelector(int BootFlags,
// Launch Segaboot // Launch Segaboot
CxbxLaunchNewXbe(chihiroSegaBootNew); CxbxLaunchNewXbe(chihiroSegaBootNew);
CxbxrShutDown(true); CxbxrShutDown(true);
} }
#endif // Chihiro wip block #endif // Chihiro wip block
CxbxKrnl_Xbe = new Xbe(xbePath.c_str()); // TODO : Instead of using the Xbe class, port Dxbx _ReadXbeBlock()
if (CxbxKrnl_Xbe->HasFatalError()) {
CxbxrAbort(CxbxKrnl_Xbe->GetError().c_str());
return false;
}
// Check the signature of the xbe
if (CxbxKrnl_Xbe->CheckSignature()) {
EmuLogInit(LOG_LEVEL::INFO, "Valid xbe signature. Xbe is legit");
}
else {
EmuLogInit(LOG_LEVEL::WARNING, "Invalid xbe signature. Homebrew, tampered or pirated xbe?");
}
// Check the integrity of the xbe sections
for (uint32_t sectionIndex = 0; sectionIndex < CxbxKrnl_Xbe->m_Header.dwSections; sectionIndex++) {
if (CxbxKrnl_Xbe->CheckSectionIntegrity(sectionIndex)) {
EmuLogInit(LOG_LEVEL::INFO, "SHA hash check of section %s successful", CxbxKrnl_Xbe->m_szSectionName[sectionIndex]);
}
else {
EmuLogInit(LOG_LEVEL::WARNING, "SHA hash of section %s doesn't match, section is corrupted", CxbxKrnl_Xbe->m_szSectionName[sectionIndex]);
}
}
// If CLI has given console type, then enforce it.
if (cli_config::hasKey(cli_config::system_chihiro)) {
EmuLogInit(LOG_LEVEL::INFO, "Auto detect is disabled, running as chihiro.");
emulate_system = SYSTEM_CHIHIRO;
}
else if (cli_config::hasKey(cli_config::system_devkit)) {
EmuLogInit(LOG_LEVEL::INFO, "Auto detect is disabled, running as devkit.");
emulate_system = SYSTEM_DEVKIT;
}
else if (cli_config::hasKey(cli_config::system_retail)) {
EmuLogInit(LOG_LEVEL::INFO, "Auto detect is disabled, running as retail.");
emulate_system = SYSTEM_XBOX;
}
// Otherwise, use auto detect method.
else {
// 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:
emulate_system = SYSTEM_CHIHIRO;
break;
case XbeType::xtDebug:
emulate_system = SYSTEM_DEVKIT;
break;
case XbeType::xtRetail:
emulate_system = SYSTEM_XBOX;
break;
DEFAULT_UNREACHABLE;
}
}
EmuLogInit(LOG_LEVEL::INFO, "Host's compatible system types: %2X", reserved_systems);
// If the system to emulate isn't supported on host, enforce failure.
if (!isSystemFlagSupport(reserved_systems, emulate_system)) {
CxbxrAbort("Unable to emulate system type due to host is not able to reserve required memory ranges.");
return false;
}
// Clear emulation system from reserved systems so all unneeded memory ranges can be freed.
reserved_systems &= ~emulate_system;
// Once we have determine which system type to run as, enforce it in future reboots. // Once we have determine which system type to run as, enforce it in future reboots.
if ((BootFlags & BOOT_QUICK_REBOOT) == 0) { if ((BootFlags & BOOT_QUICK_REBOOT) == 0) {
const char* system_str = GetSystemTypeToStr(emulate_system); const char* system_str = GetSystemTypeToStr(emulate_system);
@ -1220,7 +1165,7 @@ static void CxbxrKrnlInitHacks()
g_pCertificate = &CxbxKrnl_Xbe->m_Certificate; g_pCertificate = &CxbxKrnl_Xbe->m_Certificate;
// Initialize timer subsystem // Initialize timer subsystem
Timer_Init(); timer_init();
// for unicode conversions // for unicode conversions
setlocale(LC_ALL, "English"); setlocale(LC_ALL, "English");
// Initialize DPC global // Initialize DPC global
@ -1362,10 +1307,11 @@ static void CxbxrKrnlInitHacks()
} }
xbox::PsInitSystem(); xbox::PsInitSystem();
xbox::KiInitSystem(); xbox::KiInitSystem();
xbox::RtlInitSystem();
// initialize graphics // initialize graphics
EmuLogInit(LOG_LEVEL::DEBUG, "Initializing render window."); 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 // Now process the boot flags to see if there are any special conditions to handle
if (BootFlags & BOOT_EJECT_PENDING) {} // TODO if (BootFlags & BOOT_EJECT_PENDING) {} // TODO
@ -1474,23 +1420,17 @@ static void CxbxrKrnlInitHacks()
#endif #endif
EmuX86_Init(); EmuX86_Init();
// Create the interrupt processing thread // Start the event thread
xbox::HANDLE hThread; xbox::HANDLE hThread;
CxbxrCreateThread(&hThread, xbox::zeroptr, CxbxKrnlInterruptThread, xbox::zeroptr, FALSE); xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, system_events, xbox::zeroptr, FALSE);
// Start the kernel clock thread // Launch the xbe
TimerObject* KernelClockThr = Timer_Create(CxbxKrnlClockThread, nullptr, "Kernel clock thread", true);
Timer_Start(KernelClockThr, SCALE_MS_IN_NS);
xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, CxbxLaunchXbe, Entry, FALSE); xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, CxbxLaunchXbe, Entry, FALSE);
EmuKeFreePcr(); xbox::KeRaiseIrqlToDpcLevel();
__asm add esp, Host2XbStackSizeReserved; while (true) {
xbox::KeWaitForDpc();
// This will wait forever ExecuteDpcQueue();
std::condition_variable cv; }
std::mutex m;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [] { return false; });
} }
// REMARK: the following is useless, but PatrickvL has asked to keep it for documentation purposes // REMARK: the following is useless, but PatrickvL has asked to keep it for documentation purposes
@ -1512,7 +1452,7 @@ void CxbxrKrnlSuspendThreads()
// Don't use EmuKeGetPcr because that asserts kpcr // Don't use EmuKeGetPcr because that asserts kpcr
xbox::KPCR* Pcr = reinterpret_cast<xbox::PKPCR>(__readfsdword(TIB_ArbitraryDataSlot)); xbox::KPCR* Pcr = reinterpret_cast<xbox::PKPCR>(__readfsdword(TIB_ArbitraryDataSlot));
// If there's nothing in list entry, skip this step. // If there's nothing in list entry, skip this step.
if (!ThreadListEntry) { if (!ThreadListEntry) {
return; return;

View File

@ -157,6 +157,7 @@ void CxbxKrnlNoFunc();
void InitDpcData(); // Implemented in EmuKrnlKe.cpp void InitDpcData(); // Implemented in EmuKrnlKe.cpp
bool IsDpcActive(); bool IsDpcActive();
void ExecuteDpcQueue();
/*! kernel thunk table */ /*! kernel thunk table */
extern uint32_t CxbxKrnl_KernelThunkTable[379]; extern uint32_t CxbxKrnl_KernelThunkTable[379];

View File

@ -49,11 +49,6 @@ bool g_DisablePixelShaders = false;
bool g_UseAllCores = false; bool g_UseAllCores = false;
bool g_SkipRdtscPatching = 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 Function(s)
static int ExitException(LPEXCEPTION_POINTERS e); static int ExitException(LPEXCEPTION_POINTERS e);

View File

@ -68,9 +68,6 @@ extern HWND g_hEmuWindow;
extern HANDLE g_CurrentProcessHandle; // Set in CxbxKrnlMain 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 typedef struct DUMMY_KERNEL
{ {
IMAGE_DOS_HEADER DosHeader; IMAGE_DOS_HEADER DosHeader;

View File

@ -26,6 +26,7 @@
// ****************************************************************** // ******************************************************************
#include <core\kernel\exports\xboxkrnl.h> #include <core\kernel\exports\xboxkrnl.h>
#include <chrono>
#include <unordered_map> #include <unordered_map>
#include <shared_mutex> #include <shared_mutex>
#include "Windows.h" #include "Windows.h"

View File

@ -32,6 +32,7 @@
#include "core\kernel\common\xbox.h" #include "core\kernel\common\xbox.h"
#include "cxbxr.hpp" #include "cxbxr.hpp"
#include "core\hle\Intercept.hpp" #include "core\hle\Intercept.hpp"
#include "EmuShared.h"
PCIBus* g_PCIBus; PCIBus* g_PCIBus;
SMBus* g_SMBus; SMBus* g_SMBus;
@ -42,6 +43,7 @@ NVNetDevice* g_NVNet;
NV2ADevice* g_NV2A; NV2ADevice* g_NV2A;
ADM1032Device* g_ADM1032; ADM1032Device* g_ADM1032;
USBDevice* g_USB0; USBDevice* g_USB0;
MediaBoard* g_MediaBoard;
MCPXRevision MCPXRevisionFromHardwareModel(HardwareModel hardwareModel) MCPXRevision MCPXRevisionFromHardwareModel(HardwareModel hardwareModel)
{ {
@ -151,15 +153,23 @@ void InitXboxHardware(HardwareModel hardwareModel)
// Create devices // Create devices
g_MCPX = new MCPXDevice(mcpx_revision); g_MCPX = new MCPXDevice(mcpx_revision);
g_SMC = new SMCDevice(smc_revision, IS_CHIHIRO(hardwareModel) ? 6 : 1); // 6 = AV_PACK_STANDARD, 1 = AV_PACK_HDTV. Chihiro doesn't support HDTV!
// 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 // SMC uses different AV_PACK values than the Kernel
// See https://xboxdevwiki.net/PIC#The_AV_Pack // See https://xboxdevwiki.net/PIC#The_AV_Pack
g_EEPROM = new EEPROMDevice(); g_EEPROM = new EEPROMDevice();
g_NVNet = new NVNetDevice(); g_NVNet = new NVNetDevice();
g_NV2A = new NV2ADevice(); g_NV2A = new NV2ADevice();
g_ADM1032 = new ADM1032Device(); 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 // Connect devices to SM bus
@ -189,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(5, 0)), g_NVAPU);
//g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(6, 0)), g_AC97); //g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(6, 0)), g_AC97);
g_PCIBus->ConnectDevice(PCI_DEVID(1, PCI_DEVFN(0, 0)), g_NV2A); 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,
// 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
// 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
// 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.
// 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 // TODO : Handle other SMBUS Addresses, like PIC_ADDRESS, XCALIBUR_ADDRESS
// Resources : http://pablot.com/misc/fancontroller.cpp // Resources : http://pablot.com/misc/fancontroller.cpp

View File

@ -35,6 +35,7 @@
#include "ADM1032Device.h" // For ADM1032 #include "ADM1032Device.h" // For ADM1032
#include "devices\video\nv2a.h" // For NV2ADevice #include "devices\video\nv2a.h" // For NV2ADevice
#include "Usb\USBDevice.h" // For USBDevice #include "Usb\USBDevice.h" // For USBDevice
#include "chihiro\MediaBoard.h"
#define SMBUS_ADDRESS_MCPX 0x10 // = Write; Read = 0x11 #define SMBUS_ADDRESS_MCPX 0x10 // = Write; Read = 0x11
#define SMBUS_ADDRESS_TV_ENCODER 0x88 // = Write; Read = 0x89 #define SMBUS_ADDRESS_TV_ENCODER 0x88 // = Write; Read = 0x89
@ -83,5 +84,6 @@ extern EEPROMDevice* g_EEPROM;
extern NVNetDevice* g_NVNet; extern NVNetDevice* g_NVNet;
extern NV2ADevice* g_NV2A; extern NV2ADevice* g_NV2A;
extern USBDevice* g_USB0; extern USBDevice* g_USB0;
extern MediaBoard* g_MediaBoard;
extern void InitXboxHardware(HardwareModel hardwareModel); extern void InitXboxHardware(HardwareModel hardwareModel);

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

View 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);
}

View 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

View File

@ -476,15 +476,18 @@ void EmuNVNet_Write(xbox::addr_xt addr, uint32_t value, int size)
} }
std::thread NVNetRecvThread; 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(); g_AffinityPolicy->SetAffinityOther();
uint8_t packet[65536]; static std::unique_ptr<uint8_t[]> packet(new uint8_t[65536]);
while (true) { while (true) {
int size = g_NVNet->PCAPReceive(packet, 65536); int size = g_NVNet->PCAPReceive(packet.get(), 65536);
if (size > 0) { if (size > 0) {
EmuNVNet_DMAPacketToGuest(packet, size); EmuNVNet_DMAPacketToGuest(packet.get(), size);
} }
_mm_pause();
} }
} }
@ -527,7 +530,7 @@ void NVNetDevice::Init()
}; };
PCAPInit(); PCAPInit();
NVNetRecvThread = std::thread(NVNetRecvThreadProc, &NvNetState); NVNetRecvThread = std::thread(NVNetRecvThreadProc);
} }
void NVNetDevice::Reset() void NVNetDevice::Reset()

View File

@ -279,11 +279,6 @@ OHCI::OHCI(USBDevice* UsbObj)
OHCI_StateReset(); OHCI_StateReset();
} }
void OHCI::OHCI_FrameBoundaryWrapper(void* pVoid)
{
static_cast<OHCI*>(pVoid)->OHCI_FrameBoundaryWorker();
}
void OHCI::OHCI_FrameBoundaryWorker() void OHCI::OHCI_FrameBoundaryWorker()
{ {
OHCI_HCCA hcca; OHCI_HCCA hcca;
@ -358,7 +353,7 @@ void OHCI::OHCI_FrameBoundaryWorker()
} }
// Do SOF stuff here // Do SOF stuff here
OHCI_SOF(false); OHCI_SOF();
// Writeback HCCA // Writeback HCCA
if (OHCI_WriteHCCA(m_Registers.HcHCCA, &hcca)) { if (OHCI_WriteHCCA(m_Registers.HcHCCA, &hcca)) {
@ -877,32 +872,23 @@ void OHCI::OHCI_StateReset()
void OHCI::OHCI_BusStart() void OHCI::OHCI_BusStart()
{ {
// Create the EOF timer. // Create the EOF timer.
m_pEOFtimer = Timer_Create(OHCI_FrameBoundaryWrapper, this, "", false); m_pEOFtimer = true;
EmuLog(LOG_LEVEL::DEBUG, "Operational event"); EmuLog(LOG_LEVEL::DEBUG, "Operational event");
// SOF event // SOF event
OHCI_SOF(true); OHCI_SOF();
} }
void OHCI::OHCI_BusStop() void OHCI::OHCI_BusStop()
{ {
if (m_pEOFtimer) { m_pEOFtimer = false;
// Delete existing EOF timer
Timer_Exit(m_pEOFtimer);
}
m_pEOFtimer = nullptr;
} }
void OHCI::OHCI_SOF(bool bCreate) void OHCI::OHCI_SOF()
{ {
// set current SOF time // set current SOF time
m_SOFtime = GetTime_NS(m_pEOFtimer); m_SOFtime = get_now();
// make timer expire at SOF + 1 ms from now
if (bCreate) {
Timer_Start(m_pEOFtimer, m_UsbFrameTime);
}
OHCI_SetInterrupt(OHCI_INTR_SF); 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() void OHCI::OHCI_UpdateInterrupt()
{ {
if ((m_Registers.HcInterrupt & OHCI_INTR_MIE) && (m_Registers.HcInterruptStatus & m_Registers.HcInterrupt)) { 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 // 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 // Avoid Muldiv64 if possible
if (ticks >= m_UsbFrameTime) { if (ticks >= m_UsbFrameTime) {

View File

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

View File

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

View File

@ -35,17 +35,13 @@
#include "common\util\CxbxUtil.h" #include "common\util\CxbxUtil.h"
#define NANOSECONDS_PER_SECOND 1000000000
/* PTIMER - time measurement and time-based alarms */ /* 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 return Muldiv64(Muldiv64(get_now(),
uint64_t time = std::chrono::duration<uint64_t, std::nano>(std::chrono::steady_clock::now().time_since_epoch()).count();
return Muldiv64(Muldiv64(time,
(uint32_t)d->pramdac.core_clock_freq, // TODO : Research how this can be updated to accept uint64_t (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.denominator,
d->ptimer.numerator); d->ptimer.numerator);
} }
@ -91,6 +87,13 @@ DEVICE_WRITE32(PTIMER)
break; break;
case NV_PTIMER_INTR_EN_0: case NV_PTIMER_INTR_EN_0:
d->ptimer.enabled_interrupts = value; 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); update_irq(d);
break; break;
case NV_PTIMER_DENOMINATOR: case NV_PTIMER_DENOMINATOR:
@ -101,6 +104,7 @@ DEVICE_WRITE32(PTIMER)
break; break;
case NV_PTIMER_ALARM_0: case NV_PTIMER_ALARM_0:
d->ptimer.alarm_time = value; 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; break;
default: default:
//DEVICE_WRITE32_REG(ptimer); // Was : DEBUG_WRITE32_UNHANDLED(PTIMER); //DEVICE_WRITE32_REG(ptimer); // Was : DEBUG_WRITE32_UNHANDLED(PTIMER);

View File

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

View File

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

View File

@ -357,10 +357,15 @@ typedef struct OverlayState {
} OverlayState; } OverlayState;
typedef struct NV2AState { typedef struct NV2AState {
void(* vblank_cb)(void *);
uint64_t vblank_last;
// PCIDevice dev; // PCIDevice dev;
// qemu_irq irq; // qemu_irq irq;
bool exiting; bool exiting;
bool enable_overlay = false; bool enable_overlay = false;
bool ptimer_active = false;
uint64_t ptimer_last;
uint64_t ptimer_period;
// VGACommonState vga; // VGACommonState vga;
// GraphicHwOps hw_ops; // GraphicHwOps hw_ops;

View File

@ -57,10 +57,15 @@ extern std::atomic_bool g_bEnableAllInterrupts;
static int field_pin = 0; static int field_pin = 0;
static thread_local bool g_tls_isEmuX86Managed;
uint32_t EmuX86_IORead(xbox::addr_xt addr, int size) uint32_t EmuX86_IORead(xbox::addr_xt addr, int size)
{ {
// If we are running a Chihiro game, emulate the Chihiro LPC device
if (g_bIsChihiro) {
if (addr >= 0x4000 && addr <= 0x40FF) {
return g_MediaBoard->LpcRead(addr, size);
}
}
switch (addr) { switch (addr) {
case 0x8008: { // TODO : Move 0x8008 TIMER to a device case 0x8008: { // TODO : Move 0x8008 TIMER to a device
if (size == sizeof(uint32_t)) { if (size == sizeof(uint32_t)) {
@ -95,6 +100,14 @@ uint32_t EmuX86_IORead(xbox::addr_xt addr, int size)
void EmuX86_IOWrite(xbox::addr_xt addr, uint32_t value, int size) void EmuX86_IOWrite(xbox::addr_xt addr, uint32_t value, int size)
{ {
// If we are running a Chihiro game, emulate the Chihiro LPC device
if (g_bIsChihiro) {
if (addr >= 0x4000 && addr <= 0x40FF) {
g_MediaBoard->LpcWrite(addr, value, size);
return;
}
}
// Pass the IO Write to the PCI Bus, this will handle devices with BARs set to IO addresses // Pass the IO Write to the PCI Bus, this will handle devices with BARs set to IO addresses
if (g_PCIBus->IOWrite(addr, value, size)) { if (g_PCIBus->IOWrite(addr, value, size)) {
return; return;
@ -197,11 +210,8 @@ uint32_t EmuX86_Read(xbox::addr_xt addr, int size)
return value; return value;
} }
// EmuX86 is not suppose to do direct read to host memory and should be handle from // EmuX86 should not directly access host memory.
// redirect from above statements. If it doesn't meet any requirement, then should be EmuLog(LOG_LEVEL::WARNING, "EmuX86_Read(0x%08X, %d) [Unhandled]", addr, size);
// handle as possible fatal crash instead of return corrupt value.
g_tls_isEmuX86Managed = false;
return 0; return 0;
} }
@ -223,10 +233,8 @@ void EmuX86_Write(xbox::addr_xt addr, uint32_t value, int size)
return; return;
} }
// EmuX86 is not suppose to do direct write to host memory and should be handle from // EmuX86 should not directly access host memory.
// redirect from above statements. If it doesn't meet any requirement, then should be EmuLog(LOG_LEVEL::WARNING, "EmuX86_Write(0x%08X, 0x%08X, %d) [Unhandled]", addr, value, size);
// handle as possible fatal crash instead of set corrupt value.
g_tls_isEmuX86Managed = false;
} }
int ContextRecordOffsetByRegisterType[/*_RegisterType*/R_DR7 + 1] = { 0 }; int ContextRecordOffsetByRegisterType[/*_RegisterType*/R_DR7 + 1] = { 0 };
@ -2928,7 +2936,6 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e)
// However, if for any reason, an opcode operand cannot be read from or written to, // However, if for any reason, an opcode operand cannot be read from or written to,
// that case may be logged, but it shouldn't fail the opcode handler. // that case may be logged, but it shouldn't fail the opcode handler.
_DInst info; _DInst info;
g_tls_isEmuX86Managed = true;
DWORD StartingEip = e->ContextRecord->Eip; DWORD StartingEip = e->ContextRecord->Eip;
EmuLog(LOG_LEVEL::DEBUG, "Starting instruction emulation from 0x%08X", e->ContextRecord->Eip); EmuLog(LOG_LEVEL::DEBUG, "Starting instruction emulation from 0x%08X", e->ContextRecord->Eip);
@ -3294,15 +3301,11 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e)
return true; return true;
} // switch info.opcode } // switch info.opcode
if (g_tls_isEmuX86Managed) {
e->ContextRecord->Eip += info.size; e->ContextRecord->Eip += info.size;
}
else {
break;
}
} // while true } // while true
return g_tls_isEmuX86Managed; return true;
opcode_error: opcode_error:
EmuLog(LOG_LEVEL::WARNING, "0x%08X: Error while handling instruction %s (%u)", e->ContextRecord->Eip, Distorm_OpcodeString(info.opcode), info.opcode); EmuLog(LOG_LEVEL::WARNING, "0x%08X: Error while handling instruction %s (%u)", e->ContextRecord->Eip, Distorm_OpcodeString(info.opcode), info.opcode);

View File

@ -64,7 +64,7 @@ INT_PTR CALLBACK DlgAboutProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lPa
SendMessageW(hWndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon); SendMessageW(hWndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
// Build the Tab Control // Build the Tab Control
constexpr size_t text_len = longest_str({ "About", "Contributors", "License" }) + 1; const size_t text_len = longest_str({ "About", "Contributors", "License" }) + 1;
char text[text_len]; char text[text_len];
TCITEM tabInfo; TCITEM tabInfo;
memset(&tabInfo, 0, sizeof(tabInfo)); memset(&tabInfo, 0, sizeof(tabInfo));

View File

@ -56,6 +56,8 @@
#undef GetSystemMetrics // Force remove DirectX 8's multimon.h defined function (redirect to xGetSystemMetrics). #undef GetSystemMetrics // Force remove DirectX 8's multimon.h defined function (redirect to xGetSystemMetrics).
#include <WinUser.h> // For GetSystemMetrics #include <WinUser.h> // For GetSystemMetrics
#include <dwmapi.h> // For DwmSetWindowAttribute
#include <io.h> #include <io.h>
#include <shlobj.h> #include <shlobj.h>
@ -68,6 +70,9 @@
#define XBOX_LED_FLASH_PERIOD 176 // if you know a more accurate value, put it here #define XBOX_LED_FLASH_PERIOD 176 // if you know a more accurate value, put it here
static const char* popup_force_blank_console_type = "By force using the %s console type may cause side effects and may not be used to report to the game compatibility website.";
static const char* popup_will_not_take_effect_until_the_next_emulation = "This will not take effect until the next time emulation is started.";
static int gameLogoWidth, gameLogoHeight; static int gameLogoWidth, gameLogoHeight;
static int splashLogoWidth, splashLogoHeight; static int splashLogoWidth, splashLogoHeight;
@ -323,6 +328,10 @@ LRESULT CALLBACK WndMain::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lP
ChangeWindowMessageFilterEx(hwnd, WM_COPYDATA, MSGFLT_ALLOW, nullptr); ChangeWindowMessageFilterEx(hwnd, WM_COPYDATA, MSGFLT_ALLOW, nullptr);
ChangeWindowMessageFilterEx(hwnd, 0x0049, MSGFLT_ALLOW, nullptr); ChangeWindowMessageFilterEx(hwnd, 0x0049, MSGFLT_ALLOW, nullptr);
// Remove rounded corners from the render window on Windows 11
const DWM_WINDOW_CORNER_PREFERENCE corner_preference = DWMWCP_DONOTROUND;
DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &corner_preference, sizeof(corner_preference));
m_bCreated = true; m_bCreated = true;
} }
break; break;
@ -394,6 +403,13 @@ LRESULT CALLBACK WndMain::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lP
}; };
break; // added per PVS suggestion. break; // added per PVS suggestion.
case WM_TIMECHANGE:
{
ipc_send_kernel_update(IPC_UPDATE_KERNEL::CONFIG_CHANGE_TIME, 0, reinterpret_cast<std::uintptr_t>(m_hwndChild));
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
break;
case WM_TIMER: case WM_TIMER:
{ {
switch (wParam) switch (wParam)
@ -965,6 +981,49 @@ LRESULT CALLBACK WndMain::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lP
} }
break; break;
case ID_SETTINGS_CONFIG_CONT_AUTO:
{
g_Settings->m_gui.ConsoleTypeToggle = EMU_CONSOLE_TYPE_AUTO;
if (m_bIsStarted) {
PopupInfo(m_hwnd, popup_will_not_take_effect_until_the_next_emulation);
}
RefreshMenus();
}
break;
case ID_SETTINGS_CONFIG_CONT_RETAIL:
{
g_Settings->m_gui.ConsoleTypeToggle = EMU_CONSOLE_TYPE_RETAIL;
PopupWarning(m_hwnd, popup_force_blank_console_type, "retail");
if (m_bIsStarted) {
PopupInfo(m_hwnd, popup_will_not_take_effect_until_the_next_emulation);
}
RefreshMenus();
}
break;
case ID_SETTINGS_CONFIG_CONT_DEVKIT:
{
g_Settings->m_gui.ConsoleTypeToggle = EMU_CONSOLE_TYPE_DEVKIT;
PopupWarning(m_hwnd, popup_force_blank_console_type, "devkit");
if (m_bIsStarted) {
PopupInfo(m_hwnd, popup_will_not_take_effect_until_the_next_emulation);
}
RefreshMenus();
}
break;
case ID_SETTINGS_CONFIG_CONT_CHIHIRO:
{
g_Settings->m_gui.ConsoleTypeToggle = EMU_CONSOLE_TYPE_CHIHIRO;
PopupWarning(m_hwnd, popup_force_blank_console_type, "chihiro");
if (m_bIsStarted) {
PopupInfo(m_hwnd, popup_will_not_take_effect_until_the_next_emulation);
}
RefreshMenus();
}
break;
case ID_SETTINGS_CONFIG_DLOCCUSTOM: case ID_SETTINGS_CONFIG_DLOCCUSTOM:
{ {
char szDir[MAX_PATH]; char szDir[MAX_PATH];
@ -1092,7 +1151,7 @@ LRESULT CALLBACK WndMain::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lP
else { else {
g_Settings->m_core.KrnlDebugMode = DM_NONE; g_Settings->m_core.KrnlDebugMode = DM_NONE;
} }
PopupInfo(m_hwnd, "This will not take effect until the next time emulation is started."); PopupInfo(m_hwnd, popup_will_not_take_effect_until_the_next_emulation);
RefreshMenus(); RefreshMenus();
@ -1699,6 +1758,36 @@ void WndMain::RefreshMenus()
chk_flag = (g_Settings->m_hacks.SkipRdtscPatching) ? MF_CHECKED : MF_UNCHECKED; chk_flag = (g_Settings->m_hacks.SkipRdtscPatching) ? MF_CHECKED : MF_UNCHECKED;
CheckMenuItem(settings_menu, ID_HACKS_SKIPRDTSCPATCHING, chk_flag); CheckMenuItem(settings_menu, ID_HACKS_SKIPRDTSCPATCHING, chk_flag);
switch (g_Settings->m_gui.ConsoleTypeToggle) {
case EMU_CONSOLE_TYPE_AUTO:
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_AUTO, MF_CHECKED);
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_RETAIL, MF_UNCHECKED);
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_DEVKIT, MF_UNCHECKED);
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_CHIHIRO, MF_UNCHECKED);
break;
case EMU_CONSOLE_TYPE_RETAIL:
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_AUTO, MF_UNCHECKED);
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_RETAIL, MF_CHECKED);
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_DEVKIT, MF_UNCHECKED);
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_CHIHIRO, MF_UNCHECKED);
break;
case EMU_CONSOLE_TYPE_DEVKIT:
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_AUTO, MF_UNCHECKED);
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_RETAIL, MF_UNCHECKED);
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_DEVKIT, MF_CHECKED);
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_CHIHIRO, MF_UNCHECKED);
break;
case EMU_CONSOLE_TYPE_CHIHIRO:
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_AUTO, MF_UNCHECKED);
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_RETAIL, MF_UNCHECKED);
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_DEVKIT, MF_UNCHECKED);
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_CONT_CHIHIRO, MF_CHECKED);
break;
}
switch (g_Settings->m_gui.DataStorageToggle) { switch (g_Settings->m_gui.DataStorageToggle) {
case CXBX_DATA_APPDATA: case CXBX_DATA_APPDATA:
CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCAPPDATA, MF_CHECKED); CheckMenuItem(settings_menu, ID_SETTINGS_CONFIG_DLOCAPPDATA, MF_CHECKED);
@ -2237,6 +2326,20 @@ void WndMain::StartEmulation(HWND hwndParent, DebuggerState LocalDebuggerState /
cli_config::SetLoad(m_XbeFilename); cli_config::SetLoad(m_XbeFilename);
cli_config::SetValue(cli_config::hwnd, hwndParent); cli_config::SetValue(cli_config::hwnd, hwndParent);
cli_config::SetValue(cli_config::debug_mode, g_Settings->m_core.KrnlDebugMode); cli_config::SetValue(cli_config::debug_mode, g_Settings->m_core.KrnlDebugMode);
cli_config::ClearSystemType(); // Require to reset system type in GUI in order to choose the new system option.
if (g_Settings->m_gui.ConsoleTypeToggle > 0) {
switch (g_Settings->m_gui.ConsoleTypeToggle) {
case EMU_CONSOLE_TYPE_RETAIL:
cli_config::SetSystemType(cli_config::system_retail);
break;
case EMU_CONSOLE_TYPE_DEVKIT:
cli_config::SetSystemType(cli_config::system_devkit);
break;
case EMU_CONSOLE_TYPE_CHIHIRO:
cli_config::SetSystemType(cli_config::system_chihiro);
break;
}
}
if (g_Settings->m_core.KrnlDebugMode == DM_FILE) { if (g_Settings->m_core.KrnlDebugMode == DM_FILE) {
cli_config::SetValue(cli_config::debug_file, g_Settings->m_core.szKrnlDebug); cli_config::SetValue(cli_config::debug_file, g_Settings->m_core.szKrnlDebug);
} }

View File

@ -31,6 +31,7 @@
#include "gui\resource\ResCxbx.h" #include "gui\resource\ResCxbx.h"
#include "input\InputWindow.h" #include "input\InputWindow.h"
#include "common\input\LibusbDevice.h" #include "common\input\LibusbDevice.h"
#include <chrono>
static LibusbInputWindow *g_InputWindow = nullptr; static LibusbInputWindow *g_InputWindow = nullptr;

View File

@ -763,6 +763,13 @@ BEGIN
MENUITEM "Config &Network...", ID_SETTINGS_CONFIG_NETWORK,MFT_STRING,MFS_ENABLED MENUITEM "Config &Network...", ID_SETTINGS_CONFIG_NETWORK,MFT_STRING,MFS_ENABLED
MENUITEM "Config &Eeprom...", ID_SETTINGS_CONFIG_EEPROM,MFT_STRING,MFS_ENABLED MENUITEM "Config &Eeprom...", ID_SETTINGS_CONFIG_EEPROM,MFT_STRING,MFS_ENABLED
MENUITEM "Config &Logging...", ID_SETTINGS_CONFIG_LOGGING,MFT_STRING,MFS_ENABLED MENUITEM "Config &Logging...", ID_SETTINGS_CONFIG_LOGGING,MFT_STRING,MFS_ENABLED
POPUP "Config &Console Type..."
BEGIN
MENUITEM "&Auto", ID_SETTINGS_CONFIG_CONT_AUTO
MENUITEM "&Retail", ID_SETTINGS_CONFIG_CONT_RETAIL
MENUITEM "&Devkit", ID_SETTINGS_CONFIG_CONT_DEVKIT
MENUITEM "&Chihiro", ID_SETTINGS_CONFIG_CONT_CHIHIRO
END
POPUP "Config &Data Location...", 65535,MFT_STRING,MFS_ENABLED POPUP "Config &Data Location...", 65535,MFT_STRING,MFS_ENABLED
BEGIN BEGIN
MENUITEM "Store in AppData", ID_SETTINGS_CONFIG_DLOCAPPDATA,MFT_STRING,MFS_ENABLED MENUITEM "Store in AppData", ID_SETTINGS_CONFIG_DLOCAPPDATA,MFT_STRING,MFS_ENABLED

View File

@ -353,6 +353,11 @@
#define ID_SETTINGS_EXPERIMENTAL 40113 #define ID_SETTINGS_EXPERIMENTAL 40113
#define ID_SETTINGS_IGNOREINVALIDXBESIG 40114 #define ID_SETTINGS_IGNOREINVALIDXBESIG 40114
#define ID_SETTINGS_IGNOREINVALIDXBESEC 40115 #define ID_SETTINGS_IGNOREINVALIDXBESEC 40115
#define ID_SYNC_TIME_CHANGE 40116
#define ID_SETTINGS_CONFIG_CONT_AUTO 40117
#define ID_SETTINGS_CONFIG_CONT_RETAIL 40118
#define ID_SETTINGS_CONFIG_CONT_DEVKIT 40119
#define ID_SETTINGS_CONFIG_CONT_CHIHIRO 40120
#define IDC_STATIC -1 #define IDC_STATIC -1
// Next default values for new objects // Next default values for new objects
@ -360,7 +365,7 @@
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 139 #define _APS_NEXT_RESOURCE_VALUE 139
#define _APS_NEXT_COMMAND_VALUE 40116 #define _APS_NEXT_COMMAND_VALUE 40121
#define _APS_NEXT_CONTROL_VALUE 1308 #define _APS_NEXT_CONTROL_VALUE 1308
#define _APS_NEXT_SYMED_VALUE 109 #define _APS_NEXT_SYMED_VALUE 109
#endif #endif