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>
This commit is contained in:
parent
9241bec768
commit
f894d31332
|
@ -43,3 +43,7 @@
|
|||
[submodule "import/nv2a_vsh_cpu"]
|
||||
path = import/nv2a_vsh_cpu
|
||||
url = https://github.com/abaire/nv2a_vsh_cpu.git
|
||||
[submodule "import/mio"]
|
||||
path = import/mio
|
||||
url = https://github.com/mandreyel/mio.git
|
||||
shadow = true
|
||||
|
|
|
@ -22,6 +22,8 @@ add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/XbSymbolDatabase")
|
|||
|
||||
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/SDL2" EXCLUDE_FROM_ALL)
|
||||
|
||||
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/import/mio" EXCLUDE_FROM_ALL)
|
||||
|
||||
# Cxbx-Reloaded projects
|
||||
set(CXBXR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
|
@ -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/XbInternalStruct.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/Intercept.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/JVS/JVS.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/Patches.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/XACTENG/XactEng.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/core/hle/XGRAPHIC/XGraphic.cpp"
|
||||
|
@ -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/PatchRdtsc.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/ADM1032Device.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/Chihiro/JvsIO.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/Chihiro/MediaBoard.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/EEPROMDevice.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/network/NVNetDevice.cpp"
|
||||
"${CXBXR_ROOT_DIR}/src/devices/MCPXDevice.cpp"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 3f86a95c0784d73ce6815237ec33ed25f233b643
|
|
@ -42,6 +42,9 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
LTC_NO_PRNGS
|
||||
LTC_NO_MISC
|
||||
LTC_NO_PROTOTYPES
|
||||
|
||||
# Enable Chihiro work
|
||||
CHIHIRO_WORK
|
||||
)
|
||||
|
||||
# Reference: https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically
|
||||
|
@ -176,6 +179,7 @@ target_link_libraries(cxbx
|
|||
SDL2
|
||||
imgui
|
||||
libusb
|
||||
mio::mio_min_winapi
|
||||
|
||||
${WINS_LIB}
|
||||
)
|
||||
|
|
|
@ -48,6 +48,9 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|||
|
||||
# Use inline XXHash version
|
||||
XXH_INLINE_ALL
|
||||
|
||||
# Enable Chihiro work
|
||||
CHIHIRO_WORK
|
||||
)
|
||||
add_compile_options(
|
||||
/EHs
|
||||
|
@ -172,6 +175,7 @@ target_link_libraries(cxbxr-emu
|
|||
imgui
|
||||
libusb
|
||||
nv2a_vsh_emulator
|
||||
mio::mio_min_winapi
|
||||
|
||||
${WINS_LIB}
|
||||
)
|
||||
|
|
|
@ -3141,6 +3141,12 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(Direct3D_CreateDevice)
|
|||
|
||||
Direct3D_CreateDevice_Start(pPresentationParameters);
|
||||
|
||||
// HACK: Disable Software VertexProcessing (Fixes CreateDevice failure in Chihiro titles)
|
||||
if (BehaviorFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING) {
|
||||
BehaviorFlags &= ~D3DCREATE_SOFTWARE_VERTEXPROCESSING;
|
||||
BehaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
|
||||
}
|
||||
|
||||
// Only then call Xbox CreateDevice function
|
||||
hresult_xt hRet = XB_TRMP(Direct3D_CreateDevice)(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface);
|
||||
|
||||
|
|
|
@ -0,0 +1,672 @@
|
|||
// 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();
|
||||
}
|
||||
|
||||
Sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JVS_Init()
|
||||
{
|
||||
// Init Jvs IO board
|
||||
g_pJvsIo = new JvsIo(&ChihiroBaseBoardState.JvsSense);
|
||||
|
||||
std::string romPath = g_DataFilePath + std::string("\\EmuDisk\\Chihiro");
|
||||
std::string baseBoardQcFirmwarePath = "ic10_g24lc64.bin";
|
||||
std::string baseBoardScFirmwarePath = "pc20_g24lc64.bin";
|
||||
std::string baseBoardEepromPath = "ic11_24lc024.bin";
|
||||
std::string baseBoardBackupRamPath = "backup_ram.bin";
|
||||
|
||||
if (!JVS_LoadFile((romPath + "\\" + baseBoardQcFirmwarePath).c_str(), g_BaseBoardQcFirmware)) {
|
||||
CxbxrAbort("Failed to load base board firmware: %s", baseBoardQcFirmwarePath.c_str());
|
||||
}
|
||||
|
||||
if (!JVS_LoadFile((romPath + "\\" + baseBoardScFirmwarePath).c_str(), g_BaseBoardScFirmware)) {
|
||||
CxbxrAbort("Failed to load base board qc firmware: %s", baseBoardScFirmwarePath.c_str());
|
||||
}
|
||||
|
||||
if (!JVS_LoadFile((romPath + "\\" + baseBoardEepromPath).c_str(), g_BaseBoardEeprom)) {
|
||||
CxbxrAbort("Failed to load base board EEPROM: %s", baseBoardEepromPath.c_str());
|
||||
}
|
||||
|
||||
// backup ram is a special case, we can create it automatically if it doesn't exist
|
||||
if (!std::filesystem::exists(romPath + "\\" + baseBoardBackupRamPath)) {
|
||||
FILE *fp = fopen((romPath + "\\" + baseBoardBackupRamPath).c_str(), "w");
|
||||
if (fp == nullptr) {
|
||||
CxbxrAbort("Could not create Backup File: %s", baseBoardBackupRamPath.c_str());
|
||||
}
|
||||
|
||||
// Create 128kb empty file for backup ram
|
||||
fseek(fp, (128 * 1024) - 1, SEEK_SET);
|
||||
fputc('\0', fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (!JVS_LoadFile((romPath + "\\" + baseBoardBackupRamPath).c_str(), g_BaseBoardBackupMemory)) {
|
||||
CxbxrAbort("Failed to load base board BACKUP RAM: %s", baseBoardBackupRamPath.c_str());
|
||||
}
|
||||
|
||||
// Determine which version of JVS_SendCommand this title is using and derive the offset
|
||||
// TODO: Extract this into a function and also locate PINSB
|
||||
static int JvsSendCommandVersion = -1;
|
||||
g_pPINSA = nullptr;
|
||||
g_pPINSB = nullptr;
|
||||
|
||||
auto JvsSendCommandOffset1 = (uintptr_t)GetXboxSymbolPointer("JVS_SendCommand");
|
||||
auto JvsSendCommandOffset2 = (uintptr_t)GetXboxSymbolPointer("JVS_SendCommand2");
|
||||
auto JvsSendCommandOffset3 = (uintptr_t)GetXboxSymbolPointer("JVS_SendCommand3");
|
||||
|
||||
if (JvsSendCommandOffset1) {
|
||||
JvsSendCommandVersion = 1;
|
||||
g_pPINSA = *(DWORD**)(JvsSendCommandOffset1 + 0x2A0);
|
||||
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
|
||||
}
|
||||
|
||||
if (JvsSendCommandOffset2) {
|
||||
JvsSendCommandVersion = 2;
|
||||
g_pPINSA = *(DWORD**)(JvsSendCommandOffset2 + 0x312);
|
||||
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
|
||||
}
|
||||
|
||||
if (JvsSendCommandOffset3) {
|
||||
JvsSendCommandVersion = 3;
|
||||
g_pPINSA = *(DWORD**)(JvsSendCommandOffset3 + 0x307);
|
||||
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
|
||||
|
||||
if ((DWORD)g_pPINSA > XBE_MAX_VA) {
|
||||
// This was invalid, we must have the other varient of SendCommand3 (SEGABOOT)
|
||||
g_pPINSA = *(DWORD**)(JvsSendCommandOffset3 + 0x302);
|
||||
g_pPINSB = (DWORD*)((DWORD)g_pPINSA - 8);
|
||||
}
|
||||
}
|
||||
|
||||
// Set state to a sane initial default
|
||||
ChihiroBaseBoardState.Reset();
|
||||
|
||||
// Auto-Patch Chihiro Region Flag to match the desired game
|
||||
uint8_t ®ion = (uint8_t &)g_BaseBoardQcFirmware[0x1F00];
|
||||
auto regionFlags = g_MediaBoard->GetBootId().regionFlags;
|
||||
|
||||
// The region of the system can be converted to a game region flag by doing 1 << region
|
||||
// This gives a bitmask that can be ANDed with the BootID region flags to check the games support
|
||||
if ((regionFlags & (1 << region)) == 0) {
|
||||
// The region was not compatible, so we need to patch the region flag
|
||||
// This avoids "Error 05: This game is not acceptable by main board."
|
||||
// We use USA,EXPORT,JAPAN to make sure mutiple-language games default to English first
|
||||
if (regionFlags & MB_CHIHIRO_REGION_FLAG_USA) {
|
||||
region = 2;
|
||||
}
|
||||
else if (regionFlags & MB_CHIHIRO_REGION_FLAG_EXPORT) {
|
||||
region = 3;
|
||||
}
|
||||
else if (regionFlags & MB_CHIHIRO_REGION_FLAG_JAPAN) {
|
||||
region = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn the Chihiro/JVS Input Thread
|
||||
std::thread(JvsInputThread).detach();
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JVS_SendCommand)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD Command,
|
||||
DWORD a3,
|
||||
DWORD Length,
|
||||
DWORD a5,
|
||||
DWORD a6,
|
||||
DWORD a7,
|
||||
DWORD a8
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(a1)
|
||||
LOG_FUNC_ARG(Command)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(a5)
|
||||
LOG_FUNC_ARG(a6)
|
||||
LOG_FUNC_ARG(a7)
|
||||
LOG_FUNC_ARG(a8)
|
||||
LOG_FUNC_END;
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsBACKUP_Read)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy((void*)Buffer, &g_BaseBoardBackupMemory[Offset], Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsBACKUP_Write)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy(&g_BaseBoardBackupMemory[Offset], (void*)Buffer, Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsEEPROM_Read)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG_OUT(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy((void*)Buffer, &g_BaseBoardEeprom[Offset], Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsEEPROM_Write)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG_OUT(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy(&g_BaseBoardEeprom[Offset], (void*)Buffer, Length);
|
||||
|
||||
std::error_code error;
|
||||
g_BaseBoardEeprom.sync(error);
|
||||
|
||||
if (error) {
|
||||
EmuLog(LOG_LEVEL::WARNING, "Couldn't sync EEPROM to disk");
|
||||
}
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsFirmwareDownload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG_OUT(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy((void*)Buffer, &g_BaseBoardQcFirmware[Offset], Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsFirmwareUpload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy(&g_BaseBoardQcFirmware[Offset], (void*)Buffer, Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsNodeReceivePacket)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
PDWORD Length,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG_OUT(Buffer)
|
||||
LOG_FUNC_ARG_OUT(Length)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
// Receive the packet from the connected IO board
|
||||
uint8_t DeviceId = g_pJvsIo->GetDeviceId();
|
||||
|
||||
// TODO : "Number of packets received" below might imply multiple packets might need receiving here...
|
||||
uint16_t payloadSize = (uint16_t)g_pJvsIo->ReceivePacket(&Buffer[6]);
|
||||
if (payloadSize > 0) {
|
||||
Buffer[0] = 0; // Empty header byte, ignored
|
||||
Buffer[1] = 1; // Number of packets received
|
||||
Buffer[2] = DeviceId;
|
||||
Buffer[3] = 0; // Unused
|
||||
|
||||
*Length = payloadSize + 6;
|
||||
|
||||
// Write the payload size header field
|
||||
*((uint16_t*)&Buffer[4]) = payloadSize; // Packet Length (bytes 4-5)
|
||||
// TODO : Prevent little/big endian issues here by explicitly setting Buffer[4] and Buffer[5]
|
||||
}
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsNodeSendPacket)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
DWORD Length,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
// Buffer contains two opening bytes, '00' and 'XX', where XX is the number of JVS packets to send
|
||||
// Each JVS packet is prepended with a '00' byte, the rest of the packet is as-per the JVS I/O standard.
|
||||
|
||||
// Ignore Buffer[0] (should be 0x00)
|
||||
unsigned packetCount = Buffer[1];
|
||||
uint8_t* packetPtr = &Buffer[2]; // First JVS packet starts at offset 2;
|
||||
|
||||
for (unsigned i = 0; i < packetCount; i++) {
|
||||
// Skip the separator byte (should be 0x00)
|
||||
packetPtr++;
|
||||
|
||||
// Send the packet to the connected I/O board
|
||||
size_t bytes = g_pJvsIo->SendPacket(packetPtr);
|
||||
|
||||
// Set packetPtr to the next packet
|
||||
packetPtr += bytes;
|
||||
}
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
// Binary Coded Decimal to Decimal conversion
|
||||
uint8_t BcdToUint8(uint8_t value)
|
||||
{
|
||||
return value - 6 * (value >> 4);
|
||||
}
|
||||
|
||||
uint8_t Uint8ToBcd(uint8_t value)
|
||||
{
|
||||
return value + 6 * (value / 10);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsRTC_Read)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
JvsRTCTime* pTime,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(a1)
|
||||
LOG_FUNC_ARG(a2)
|
||||
LOG_FUNC_ARG_OUT(time)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
time_t hostTime;
|
||||
struct tm* hostTimeInfo;
|
||||
time(&hostTime);
|
||||
hostTimeInfo = localtime(&hostTime);
|
||||
|
||||
memset(pTime, 0, sizeof(JvsRTCTime));
|
||||
|
||||
pTime->day = Uint8ToBcd(hostTimeInfo->tm_mday);
|
||||
pTime->month = Uint8ToBcd(hostTimeInfo->tm_mon + 1); // Chihiro month counter stats at 1
|
||||
pTime->year = Uint8ToBcd(hostTimeInfo->tm_year - 100); // Chihiro starts counting from year 2000
|
||||
|
||||
pTime->hour = Uint8ToBcd(hostTimeInfo->tm_hour);
|
||||
pTime->minute = Uint8ToBcd(hostTimeInfo->tm_min);
|
||||
pTime->second = Uint8ToBcd(hostTimeInfo->tm_sec);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsRTC_Write)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
JvsRTCTime* pTime,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(a1)
|
||||
LOG_FUNC_ARG(a2)
|
||||
LOG_FUNC_ARG_OUT(time)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScFirmwareDownload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG_OUT(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy((void*)Buffer, &g_BaseBoardScFirmware[Offset], Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScFirmwareUpload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Offset)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(a4)
|
||||
LOG_FUNC_END
|
||||
|
||||
memcpy(&g_BaseBoardScFirmware[Offset], (void*)Buffer, Length);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScReceiveMidi)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(a1)
|
||||
LOG_FUNC_ARG(a2)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScSendMidi)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(a1)
|
||||
LOG_FUNC_ARG(a2)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScReceiveRs323c)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
DWORD Length,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
DWORD WINAPI xbox::EMUPATCH(JvsScSendRs323c)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
DWORD Length,
|
||||
DWORD a3
|
||||
)
|
||||
{
|
||||
LOG_FUNC_BEGIN
|
||||
LOG_FUNC_ARG(Buffer)
|
||||
LOG_FUNC_ARG(Length)
|
||||
LOG_FUNC_ARG(a3)
|
||||
LOG_FUNC_END
|
||||
|
||||
LOG_UNIMPLEMENTED();
|
||||
|
||||
RETURN(0);
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
// ******************************************************************
|
||||
// * This file is part of the Cxbx project.
|
||||
// *
|
||||
// * Cxbx and Cxbe are free software; you can redistribute them
|
||||
// * and/or modify them under the terms of the GNU General Public
|
||||
// * License as published by the Free Software Foundation; either
|
||||
// * version 2 of the license, or (at your option) any later version.
|
||||
// *
|
||||
// * This program is distributed in the hope that it will be useful,
|
||||
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// * GNU General Public License for more details.
|
||||
// *
|
||||
// * You should have recieved a copy of the GNU General Public License
|
||||
// * along with this program; see the file COPYING.
|
||||
// * If not, write to the Free Software Foundation, Inc.,
|
||||
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
|
||||
// *
|
||||
// * (c) 2019 Luke Usher <luke.usher@outlook.com>
|
||||
// *
|
||||
// * All rights reserved
|
||||
// *
|
||||
// ******************************************************************
|
||||
#ifndef JVS_H
|
||||
#define JVS_H
|
||||
|
||||
// Used by CxbxKrnl to setup JVS roms
|
||||
void JVS_Init();
|
||||
|
||||
#include "core\hle\XAPI\Xapi.h" // For EMUPATCH
|
||||
|
||||
namespace xbox {
|
||||
|
||||
DWORD WINAPI EMUPATCH(JVS_SendCommand)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD Command,
|
||||
DWORD a3,
|
||||
DWORD Length,
|
||||
DWORD a5,
|
||||
DWORD a6,
|
||||
DWORD a7,
|
||||
DWORD a8
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsBACKUP_Read)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsBACKUP_Write)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsEEPROM_Read)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsEEPROM_Write)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsFirmwareDownload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsFirmwareUpload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsNodeReceivePacket)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
PDWORD Length,
|
||||
DWORD a3
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsNodeSendPacket)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
DWORD Length,
|
||||
DWORD a3
|
||||
);
|
||||
|
||||
typedef struct {
|
||||
UCHAR second;
|
||||
UCHAR minute;
|
||||
UCHAR hour;
|
||||
UCHAR unused_2;
|
||||
|
||||
UCHAR day;
|
||||
UCHAR month;
|
||||
UCHAR year;
|
||||
UCHAR unused_1;
|
||||
} JvsRTCTime;
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsRTC_Read)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
JvsRTCTime *time,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsRTC_Write)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
JvsRTCTime *time,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsScFirmwareDownload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsScFirmwareUpload)
|
||||
(
|
||||
DWORD Offset,
|
||||
DWORD Length,
|
||||
PUCHAR Buffer,
|
||||
DWORD a4
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsScReceiveMidi)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
DWORD a3
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsScSendMidi)
|
||||
(
|
||||
DWORD a1,
|
||||
DWORD a2,
|
||||
DWORD a3
|
||||
);
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsScReceiveRs323c)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
DWORD Length,
|
||||
DWORD a3
|
||||
);
|
||||
|
||||
|
||||
DWORD WINAPI EMUPATCH(JvsScSendRs323c)
|
||||
(
|
||||
PUCHAR Buffer,
|
||||
DWORD Length,
|
||||
DWORD a3
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -29,6 +29,7 @@
|
|||
#include "core\kernel\init\CxbxKrnl.h"
|
||||
#include "core\kernel\support\Emu.h"
|
||||
#include "core\hle\D3D8\Direct3D9/Direct3D9.h"
|
||||
#include "core\hle\JVS\JVS.h"
|
||||
#include "core\hle\DSOUND\DirectSound\DirectSound.hpp"
|
||||
#include "Patches.hpp"
|
||||
#include "Intercept.hpp"
|
||||
|
@ -375,6 +376,54 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
|
|||
//PATCH_ENTRY("timeSetEvent", xbox::EMUPATCH(timeSetEvent), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("XReadMUMetaData", xbox::EMUPATCH(XReadMUMetaData), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("XUnmountMU", xbox::EMUPATCH(XUnmountMU), PATCH_ALWAYS),
|
||||
|
||||
// JVS Functions
|
||||
PATCH_ENTRY("JVS_SendCommand", xbox::EMUPATCH(JVS_SendCommand), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JVS_SendCommand2", xbox::EMUPATCH(JVS_SendCommand), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JVS_SendCommand3", xbox::EMUPATCH(JVS_SendCommand), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsBACKUP_Read", xbox::EMUPATCH(JvsBACKUP_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsBACKUP_Read2", xbox::EMUPATCH(JvsBACKUP_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsBACKUP_Read3", xbox::EMUPATCH(JvsBACKUP_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsBACKUP_Write", xbox::EMUPATCH(JvsBACKUP_Write), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsBACKUP_Write2", xbox::EMUPATCH(JvsBACKUP_Write), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsEEPROM_Read", xbox::EMUPATCH(JvsEEPROM_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsEEPROM_Read2", xbox::EMUPATCH(JvsEEPROM_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsEEPROM_Read3", xbox::EMUPATCH(JvsEEPROM_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsEEPROM_Write", xbox::EMUPATCH(JvsEEPROM_Write), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsEEPROM_Write2", xbox::EMUPATCH(JvsEEPROM_Write), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsEEPROM_Write3", xbox::EMUPATCH(JvsEEPROM_Write), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareDownload", xbox::EMUPATCH(JvsFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareDownload2", xbox::EMUPATCH(JvsFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareDownload3", xbox::EMUPATCH(JvsFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareDownload4", xbox::EMUPATCH(JvsFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareUpload", xbox::EMUPATCH(JvsFirmwareUpload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareUpload2", xbox::EMUPATCH(JvsFirmwareUpload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareUpload3", xbox::EMUPATCH(JvsFirmwareUpload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsFirmwareUpload4", xbox::EMUPATCH(JvsFirmwareUpload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsNodeReceivePacket", xbox::EMUPATCH(JvsNodeReceivePacket), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsNodeReceivePacket2", xbox::EMUPATCH(JvsNodeReceivePacket), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsNodeSendPacket", xbox::EMUPATCH(JvsNodeSendPacket), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsNodeSendPacket2", xbox::EMUPATCH(JvsNodeSendPacket), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsRTC_Read", xbox::EMUPATCH(JvsRTC_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsRTC_Read2", xbox::EMUPATCH(JvsRTC_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsRTC_Read3", xbox::EMUPATCH(JvsRTC_Read), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsRTC_Write", xbox::EMUPATCH(JvsRTC_Write), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsRTC_Write2", xbox::EMUPATCH(JvsRTC_Write), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScFirmwareDownload", xbox::EMUPATCH(JvsScFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScFirmwareDownload2", xbox::EMUPATCH(JvsScFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScFirmwareDownload3", xbox::EMUPATCH(JvsScFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScFirmwareDownload4", xbox::EMUPATCH(JvsScFirmwareDownload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScFirmwareUpload", xbox::EMUPATCH(JvsScFirmwareUpload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScFirmwareUpload2", xbox::EMUPATCH(JvsScFirmwareUpload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScFirmwareUpload3", xbox::EMUPATCH(JvsScFirmwareUpload), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScReceiveMidi", xbox::EMUPATCH(JvsScReceiveMidi), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScReceiveMidi2", xbox::EMUPATCH(JvsScReceiveMidi), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScReceiveRs323c", xbox::EMUPATCH(JvsScReceiveRs323c), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScReceiveRs323c2", xbox::EMUPATCH(JvsScReceiveRs323c), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScSendMidi", xbox::EMUPATCH(JvsScSendMidi), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScSendMidi2", xbox::EMUPATCH(JvsScSendMidi), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScSendRs323c", xbox::EMUPATCH(JvsScSendRs323c), PATCH_ALWAYS),
|
||||
PATCH_ENTRY("JvsScSendRs323c2", xbox::EMUPATCH(JvsScSendRs323c), PATCH_ALWAYS),
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, subhook::Hook> g_FunctionHooks;
|
||||
|
|
|
@ -248,6 +248,14 @@ XBSYSAPI EXPORTNUM(66) xbox::ntstatus_xt NTAPI xbox::IoCreateFile
|
|||
LOG_FUNC_ARG(Options)
|
||||
LOG_FUNC_END;
|
||||
|
||||
// If we are emulating the Chihiro, we need to hook mbcom, so return an easily identifable handle
|
||||
if (g_bIsChihiro) {
|
||||
if (strncmp(ObjectAttributes->ObjectName->Buffer, DriveMbcom.c_str(), DriveMbcom.length()) == 0) {
|
||||
*FileHandle = CHIHIRO_MBCOM_HANDLE;
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
NativeObjectAttributes nativeObjectAttributes;
|
||||
|
||||
// If we are NOT accessing a directory, and we match a partition path, we need to redirect to a partition.bin file
|
||||
|
|
|
@ -47,6 +47,7 @@ namespace NtDll
|
|||
#include "core\kernel\support\EmuFile.h" // For EmuNtSymbolicLinkObject, NtStatusToString(), etc.
|
||||
#include "core\kernel\memory-manager\VMManager.h" // For g_VMManager
|
||||
#include "core\kernel\support\NativeHandle.h"
|
||||
#include "devices\Xbox.h"
|
||||
#include "CxbxDebugger.h"
|
||||
|
||||
#pragma warning(disable:4005) // Ignore redefined status values
|
||||
|
@ -1711,6 +1712,16 @@ XBSYSAPI EXPORTNUM(219) xbox::ntstatus_xt NTAPI xbox::NtReadFile
|
|||
CxbxDebugger::ReportFileRead(FileHandle, Length, Offset);
|
||||
}
|
||||
|
||||
// If we are emulating the Chihiro, we need to hook mbcom
|
||||
if (g_bIsChihiro && FileHandle == CHIHIRO_MBCOM_HANDLE) {
|
||||
g_MediaBoard->ComRead(ByteOffset->QuadPart, Buffer, Length);
|
||||
|
||||
// Update the Status Block
|
||||
IoStatusBlock->Status = STATUS_SUCCESS;
|
||||
IoStatusBlock->Information = Length;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (ApcRoutine != nullptr) {
|
||||
// Pack the original parameters to a wrapped context for a custom APC routine
|
||||
CxbxIoDispatcherContext* cxbxContext = new CxbxIoDispatcherContext(IoStatusBlock, ApcRoutine, ApcContext);
|
||||
|
@ -2300,6 +2311,16 @@ XBSYSAPI EXPORTNUM(236) xbox::ntstatus_xt NTAPI xbox::NtWriteFile
|
|||
CxbxDebugger::ReportFileWrite(FileHandle, Length, Offset);
|
||||
}
|
||||
|
||||
// If we are emulating the Chihiro, we need to hook mbcom
|
||||
if (g_bIsChihiro && FileHandle == CHIHIRO_MBCOM_HANDLE) {
|
||||
g_MediaBoard->ComWrite(ByteOffset->QuadPart, Buffer, Length);
|
||||
|
||||
// Update the Status Block
|
||||
IoStatusBlock->Status = STATUS_SUCCESS;
|
||||
IoStatusBlock->Information = Length;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (ApcRoutine != nullptr) {
|
||||
// Pack the original parameters to a wrapped context for a custom APC routine
|
||||
CxbxIoDispatcherContext* cxbxContext = new CxbxIoDispatcherContext(IoStatusBlock, ApcRoutine, ApcContext);
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "core\kernel\common\xbox.h"
|
||||
#include "cxbxr.hpp"
|
||||
#include "core\hle\Intercept.hpp"
|
||||
#include "EmuShared.h"
|
||||
|
||||
PCIBus* g_PCIBus;
|
||||
SMBus* g_SMBus;
|
||||
|
@ -42,6 +43,7 @@ NVNetDevice* g_NVNet;
|
|||
NV2ADevice* g_NV2A;
|
||||
ADM1032Device* g_ADM1032;
|
||||
USBDevice* g_USB0;
|
||||
MediaBoard* g_MediaBoard;
|
||||
|
||||
MCPXRevision MCPXRevisionFromHardwareModel(HardwareModel hardwareModel)
|
||||
{
|
||||
|
@ -151,7 +153,10 @@ void InitXboxHardware(HardwareModel hardwareModel)
|
|||
|
||||
// Create devices
|
||||
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
|
||||
// See https://xboxdevwiki.net/PIC#The_AV_Pack
|
||||
g_EEPROM = new EEPROMDevice();
|
||||
|
@ -160,6 +165,13 @@ void InitXboxHardware(HardwareModel hardwareModel)
|
|||
g_ADM1032 = new ADM1032Device();
|
||||
g_USB0 = new USBDevice();
|
||||
|
||||
if (IS_CHIHIRO(hardwareModel)) {
|
||||
g_MediaBoard = new MediaBoard();
|
||||
char MediaBoardMountPath[xbox::max_path];
|
||||
g_EmuShared->GetTitleMountPath(MediaBoardMountPath);
|
||||
g_MediaBoard->SetMountPath(MediaBoardMountPath);
|
||||
}
|
||||
|
||||
// Connect devices to SM bus
|
||||
g_SMBus->ConnectDevice(SMBUS_ADDRESS_SYSTEM_MICRO_CONTROLLER, g_SMC); // W 0x20 R 0x21
|
||||
g_SMBus->ConnectDevice(SMBUS_ADDRESS_EEPROM, g_EEPROM); // W 0xA8 R 0xA9
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "ADM1032Device.h" // For ADM1032
|
||||
#include "devices\video\nv2a.h" // For NV2ADevice
|
||||
#include "Usb\USBDevice.h" // For USBDevice
|
||||
#include "chihiro\MediaBoard.h"
|
||||
|
||||
#define SMBUS_ADDRESS_MCPX 0x10 // = Write; Read = 0x11
|
||||
#define SMBUS_ADDRESS_TV_ENCODER 0x88 // = Write; Read = 0x89
|
||||
|
@ -83,5 +84,6 @@ extern EEPROMDevice* g_EEPROM;
|
|||
extern NVNetDevice* g_NVNet;
|
||||
extern NV2ADevice* g_NV2A;
|
||||
extern USBDevice* g_USB0;
|
||||
extern MediaBoard* g_MediaBoard;
|
||||
|
||||
extern void InitXboxHardware(HardwareModel hardwareModel);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,158 @@
|
|||
// 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)
|
||||
{
|
||||
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);
|
||||
}
|
|
@ -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
|
|
@ -59,6 +59,13 @@ static int field_pin = 0;
|
|||
|
||||
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) {
|
||||
case 0x8008: { // TODO : Move 0x8008 TIMER to a device
|
||||
if (size == sizeof(uint32_t)) {
|
||||
|
@ -93,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)
|
||||
{
|
||||
// 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
|
||||
if (g_PCIBus->IOWrite(addr, value, size)) {
|
||||
return;
|
||||
|
|
Loading…
Reference in New Issue