Browse Source
Merge pull request #15 from bunnei/hle-services
Merge pull request #15 from bunnei/hle-services
Various fixes/improvements to HLE of 3DS services, mostly cleans up GSP call decodingpull/15/merge
35 changed files with 857 additions and 385 deletions
-
10src/citra_qt/main.cpp
-
1src/citra_qt/main.hxx
-
6src/citra_qt/main.ui
-
5src/citra_qt/ui_main.h
-
172src/common/bit_field.h
-
1src/common/common.vcxproj
-
1src/common/common.vcxproj.filters
-
4src/common/log.h
-
6src/common/log_manager.cpp
-
4src/core/CMakeLists.txt
-
33src/core/arm/disassembler/load_symbol_map.cpp
-
13src/core/arm/disassembler/load_symbol_map.h
-
39src/core/arm/interpreter/armemu.cpp
-
61src/core/arm/interpreter/armsupp.cpp
-
6src/core/arm/mmu/arm1176jzf_s_mmu.cpp
-
8src/core/core.vcxproj
-
16src/core/core.vcxproj.filters
-
70src/core/hle/config_mem.cpp
-
21src/core/hle/config_mem.h
-
50src/core/hle/coprocessor.cpp
-
11src/core/hle/coprocessor.h
-
20src/core/hle/function_wrappers.h
-
47src/core/hle/hle.cpp
-
21src/core/hle/hle.h
-
64src/core/hle/mrc.cpp
-
2src/core/hle/service/apt.cpp
-
71src/core/hle/service/gsp.cpp
-
12src/core/hle/service/service.h
-
2src/core/hle/service/srv.cpp
-
57src/core/hle/syscall.cpp
-
53src/core/loader.cpp
-
2src/core/loader.h
-
3src/core/mem_map.cpp
-
11src/core/mem_map.h
-
39src/core/mem_map_funcs.cpp
@ -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
|
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue