mirror of https://github.com/yuzu-mirror/yuzu
Merge pull request #15 from bunnei/hle-services
Various fixes/improvements to HLE of 3DS services, mostly cleans up GSP call decodingpull/8/head
commit
bdc54d0d48
@ -0,0 +1,172 @@
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/common.h"
|
||||
|
||||
/*
|
||||
* Abstract bitfield class
|
||||
*
|
||||
* Allows endianness-independent access to individual bitfields within some raw
|
||||
* integer value. The assembly generated by this class is identical to the
|
||||
* usage of raw bitfields, so it's a perfectly fine replacement.
|
||||
*
|
||||
* For BitField<X,Y,Z>, X is the distance of the bitfield to the LSB of the
|
||||
* raw value, Y is the length in bits of the bitfield. Z is an integer type
|
||||
* which determines the sign of the bitfield. Z must have the same size as the
|
||||
* raw integer.
|
||||
*
|
||||
*
|
||||
* General usage:
|
||||
*
|
||||
* Create a new union with the raw integer value as a member.
|
||||
* Then for each bitfield you want to expose, add a BitField member
|
||||
* in the union. The template parameters are the bit offset and the number
|
||||
* of desired bits.
|
||||
*
|
||||
* Changes in the bitfield members will then get reflected in the raw integer
|
||||
* value and vice-versa.
|
||||
*
|
||||
*
|
||||
* Sample usage:
|
||||
*
|
||||
* union SomeRegister
|
||||
* {
|
||||
* u32 hex;
|
||||
*
|
||||
* BitField<0,7,u32> first_seven_bits; // unsigned
|
||||
* BitField<7,8,32> next_eight_bits; // unsigned
|
||||
* BitField<3,15,s32> some_signed_fields; // signed
|
||||
* };
|
||||
*
|
||||
* This is equivalent to the little-endian specific code:
|
||||
*
|
||||
* union SomeRegister
|
||||
* {
|
||||
* u32 hex;
|
||||
*
|
||||
* struct
|
||||
* {
|
||||
* u32 first_seven_bits : 7;
|
||||
* u32 next_eight_bits : 8;
|
||||
* };
|
||||
* struct
|
||||
* {
|
||||
* u32 : 3; // padding
|
||||
* s32 some_signed_fields : 15;
|
||||
* };
|
||||
* };
|
||||
*
|
||||
*
|
||||
* Caveats:
|
||||
*
|
||||
* 1)
|
||||
* BitField provides automatic casting from and to the storage type where
|
||||
* appropriate. However, when using non-typesafe functions like printf, an
|
||||
* explicit cast must be performed on the BitField object to make sure it gets
|
||||
* passed correctly, e.g.:
|
||||
* printf("Value: %d", (s32)some_register.some_signed_fields);
|
||||
*
|
||||
* 2)
|
||||
* Not really a caveat, but potentially irritating: This class is used in some
|
||||
* packed structures that do not guarantee proper alignment. Therefore we have
|
||||
* to use #pragma pack here not to pack the members of the class, but instead
|
||||
* to break GCC's assumption that the members of the class are aligned on
|
||||
* sizeof(StorageType).
|
||||
* TODO(neobrain): Confirm that this is a proper fix and not just masking
|
||||
* symptoms.
|
||||
*/
|
||||
#pragma pack(1)
|
||||
template<std::size_t position, std::size_t bits, typename T>
|
||||
struct BitField
|
||||
{
|
||||
private:
|
||||
// This constructor might be considered ambiguous:
|
||||
// Would it initialize the storage or just the bitfield?
|
||||
// Hence, delete it. Use the assignment operator to set bitfield values!
|
||||
BitField(T val) = delete;
|
||||
|
||||
public:
|
||||
// Force default constructor to be created
|
||||
// so that we can use this within unions
|
||||
BitField() = default;
|
||||
|
||||
__forceinline BitField& operator=(T val)
|
||||
{
|
||||
storage = (storage & ~GetMask()) | ((val << position) & GetMask());
|
||||
return *this;
|
||||
}
|
||||
|
||||
__forceinline operator T() const
|
||||
{
|
||||
if (std::numeric_limits<T>::is_signed)
|
||||
{
|
||||
std::size_t shift = 8 * sizeof(T)-bits;
|
||||
return (T)(((storage & GetMask()) << (shift - position)) >> shift);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (T)((storage & GetMask()) >> position);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// StorageType is T for non-enum types and the underlying type of T if
|
||||
// T is an enumeration. Note that T is wrapped within an enable_if in the
|
||||
// former case to workaround compile errors which arise when using
|
||||
// std::underlying_type<T>::type directly.
|
||||
typedef typename std::conditional < std::is_enum<T>::value,
|
||||
std::underlying_type<T>,
|
||||
std::enable_if < true, T >> ::type::type StorageType;
|
||||
|
||||
// Unsigned version of StorageType
|
||||
typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
|
||||
|
||||
__forceinline StorageType GetMask() const
|
||||
{
|
||||
return ((~(StorageTypeU)0) >> (8 * sizeof(T)-bits)) << position;
|
||||
}
|
||||
|
||||
StorageType storage;
|
||||
|
||||
static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
|
||||
|
||||
// And, you know, just in case people specify something stupid like bits=position=0x80000000
|
||||
static_assert(position < 8 * sizeof(T), "Invalid position");
|
||||
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
|
||||
static_assert(bits > 0, "Invalid number of bits");
|
||||
};
|
||||
#pragma pack()
|
||||
@ -0,0 +1,33 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/symbols.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
|
||||
#include "core/arm/disassembler/load_symbol_map.h"
|
||||
|
||||
/*
|
||||
* Loads a symbol map file for use with the disassembler
|
||||
* @param filename String filename path of symbol map file
|
||||
*/
|
||||
void LoadSymbolMap(std::string filename) {
|
||||
std::ifstream infile(filename);
|
||||
|
||||
std::string address_str, function_name, line;
|
||||
u32 size, address;
|
||||
|
||||
while (std::getline(infile, line)) {
|
||||
std::istringstream iss(line);
|
||||
if (!(iss >> address_str >> size >> function_name)) {
|
||||
break; // Error parsing
|
||||
}
|
||||
u32 address = std::stoul(address_str, nullptr, 16);
|
||||
|
||||
Symbols::Add(address, function_name, size, 2);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
/*
|
||||
* Loads a symbol map file for use with the disassembler
|
||||
* @param filename String filename path of symbol map file
|
||||
*/
|
||||
void LoadSymbolMap(std::string filename);
|
||||
@ -0,0 +1,70 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/log.h"
|
||||
|
||||
#include "core/hle/config_mem.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace ConfigMem {
|
||||
|
||||
enum {
|
||||
KERNEL_VERSIONREVISION = 0x1FF80001,
|
||||
KERNEL_VERSIONMINOR = 0x1FF80002,
|
||||
KERNEL_VERSIONMAJOR = 0x1FF80003,
|
||||
UPDATEFLAG = 0x1FF80004,
|
||||
NSTID = 0x1FF80008,
|
||||
SYSCOREVER = 0x1FF80010,
|
||||
UNITINFO = 0x1FF80014,
|
||||
KERNEL_CTRSDKVERSION = 0x1FF80018,
|
||||
APPMEMTYPE = 0x1FF80030,
|
||||
APPMEMALLOC = 0x1FF80040,
|
||||
FIRM_VERSIONREVISION = 0x1FF80061,
|
||||
FIRM_VERSIONMINOR = 0x1FF80062,
|
||||
FIRM_VERSIONMAJOR = 0x1FF80063,
|
||||
FIRM_SYSCOREVER = 0x1FF80064,
|
||||
FIRM_CTRSDKVERSION = 0x1FF80068,
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline void Read(T &var, const u32 addr) {
|
||||
switch (addr) {
|
||||
|
||||
// Bit 0 set for Retail
|
||||
case UNITINFO:
|
||||
var = 0x00000001;
|
||||
break;
|
||||
|
||||
// Set app memory size to 64MB?
|
||||
case APPMEMALLOC:
|
||||
var = 0x04000000;
|
||||
break;
|
||||
|
||||
// Unknown - normally set to: 0x08000000 - (APPMEMALLOC + *0x1FF80048)
|
||||
// (Total FCRAM size - APPMEMALLOC - *0x1FF80048)
|
||||
case 0x1FF80044:
|
||||
var = 0x08000000 - (0x04000000 + 0x1400000);
|
||||
break;
|
||||
|
||||
// Unknown - normally set to: 0x1400000 (20MB)
|
||||
case 0x1FF80048:
|
||||
var = 0x1400000;
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(HLE, "unknown ConfigMem::Read%d @ 0x%08X", sizeof(var) * 8, addr);
|
||||
}
|
||||
}
|
||||
|
||||
// Explicitly instantiate template functions because we aren't defining this in the header:
|
||||
|
||||
template void Read<u64>(u64 &var, const u32 addr);
|
||||
template void Read<u32>(u32 &var, const u32 addr);
|
||||
template void Read<u16>(u16 &var, const u32 addr);
|
||||
template void Read<u8>(u8 &var, const u32 addr);
|
||||
|
||||
|
||||
} // namespace
|
||||
@ -0,0 +1,21 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
// Configuration memory stores various hardware/kernel configuration settings. This memory page is
|
||||
// read-only for ARM11 processes. I'm guessing this would normally be written to by the firmware/
|
||||
// bootrom. Because we're not emulating this, and essentially just "stubbing" the functionality, I'm
|
||||
// putting this as a subset of HLE for now.
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace ConfigMem {
|
||||
|
||||
template <typename T>
|
||||
inline void Read(T &var, const u32 addr);
|
||||
|
||||
} // namespace
|
||||
@ -0,0 +1,50 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/coprocessor.h"
|
||||
#include "core/hle/hle.h"
|
||||
#include "core/mem_map.h"
|
||||
#include "core/core.h"
|
||||
|
||||
namespace HLE {
|
||||
|
||||
/// Data synchronization barrier
|
||||
u32 DataSynchronizationBarrier() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Returns the coprocessor (in this case, syscore) command buffer pointer
|
||||
Addr GetThreadCommandBuffer() {
|
||||
// Called on insruction: mrc p15, 0, r0, c13, c0, 3
|
||||
return Memory::KERNEL_MEMORY_VADDR;
|
||||
}
|
||||
|
||||
/// Call an MCR (move to coprocessor from ARM register) instruction in HLE
|
||||
s32 CallMCR(u32 instruction, u32 value) {
|
||||
CoprocessorOperation operation = (CoprocessorOperation)((instruction >> 20) & 0xFF);
|
||||
ERROR_LOG(OSHLE, "unimplemented MCR instruction=0x%08X, operation=%02X, value=%08X",
|
||||
instruction, operation, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Call an MRC (move to ARM register from coprocessor) instruction in HLE
|
||||
s32 CallMRC(u32 instruction) {
|
||||
CoprocessorOperation operation = (CoprocessorOperation)((instruction >> 20) & 0xFF);
|
||||
|
||||
switch (operation) {
|
||||
|
||||
case DATA_SYNCHRONIZATION_BARRIER:
|
||||
return DataSynchronizationBarrier();
|
||||
|
||||
case CALL_GET_THREAD_COMMAND_BUFFER:
|
||||
return GetThreadCommandBuffer();
|
||||
|
||||
default:
|
||||
ERROR_LOG(OSHLE, "unimplemented MRC instruction 0x%08X", instruction);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -1,64 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/mrc.h"
|
||||
#include "core/hle/hle.h"
|
||||
#include "core/mem_map.h"
|
||||
#include "core/core.h"
|
||||
|
||||
namespace HLE {
|
||||
|
||||
enum {
|
||||
CMD_GX_REQUEST_DMA = 0x00000000,
|
||||
};
|
||||
|
||||
/// Data synchronization barrier
|
||||
u32 DataSynchronizationBarrier(u32* command_buffer) {
|
||||
u32 command = command_buffer[0];
|
||||
|
||||
switch (command) {
|
||||
|
||||
case CMD_GX_REQUEST_DMA:
|
||||
{
|
||||
u32* src = (u32*)Memory::GetPointer(command_buffer[1]);
|
||||
u32* dst = (u32*)Memory::GetPointer(command_buffer[2]);
|
||||
u32 size = command_buffer[3];
|
||||
memcpy(dst, src, size);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(OSHLE, "MRC::DataSynchronizationBarrier unknown command 0x%08X", command);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Returns the coprocessor (in this case, syscore) command buffer pointer
|
||||
Addr GetThreadCommandBuffer() {
|
||||
// Called on insruction: mrc p15, 0, r0, c13, c0, 3
|
||||
// Returns an address in OSHLE memory for the CPU to read/write to
|
||||
RETURN(CMD_BUFFER_ADDR);
|
||||
return CMD_BUFFER_ADDR;
|
||||
}
|
||||
|
||||
/// Call an MRC operation in HLE
|
||||
u32 CallMRC(ARM11_MRC_OPERATION operation) {
|
||||
switch (operation) {
|
||||
|
||||
case DATA_SYNCHRONIZATION_BARRIER:
|
||||
return DataSynchronizationBarrier((u32*)Memory::GetPointer(PARAM(0)));
|
||||
|
||||
case CALL_GET_THREAD_COMMAND_BUFFER:
|
||||
return GetThreadCommandBuffer();
|
||||
|
||||
default:
|
||||
ERROR_LOG(OSHLE, "unimplemented MRC operation 0x%02X", operation);
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Loading…
Reference in New Issue