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:
RadWolfie 2024-07-05 11:42:10 -05:00
parent 9241bec768
commit f894d31332
19 changed files with 1902 additions and 3 deletions

4
.gitmodules vendored
View File

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

View File

@ -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"

1
import/mio vendored Submodule

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

View File

@ -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}
)

View File

@ -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}
)

View File

@ -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);

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

@ -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 &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\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;

View File

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

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

View File

@ -1304,7 +1304,7 @@ static void CxbxrKrnlInitHacks()
xbox::PsInitSystem();
xbox::KiInitSystem();
xbox::RtlInitSystem();
// initialize graphics
EmuLogInit(LOG_LEVEL::DEBUG, "Initializing render window.");
CxbxInitWindow();
@ -1448,7 +1448,7 @@ void CxbxrKrnlSuspendThreads()
// Don't use EmuKeGetPcr because that asserts kpcr
xbox::KPCR* Pcr = reinterpret_cast<xbox::PKPCR>(__readfsdword(TIB_ArbitraryDataSlot));
// If there's nothing in list entry, skip this step.
if (!ThreadListEntry) {
return;

View File

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

View File

@ -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);

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

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

@ -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;