core: Remove special regions (#7211)
This commit is contained in:
parent
dc8425a986
commit
db7b929e47
|
@ -455,7 +455,6 @@ add_library(citra_core STATIC
|
||||||
loader/smdh.h
|
loader/smdh.h
|
||||||
memory.cpp
|
memory.cpp
|
||||||
memory.h
|
memory.h
|
||||||
mmio.h
|
|
||||||
movie.cpp
|
movie.cpp
|
||||||
movie.h
|
movie.h
|
||||||
nus_download.cpp
|
nus_download.cpp
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
#include "core/hle/kernel/vm_manager.h"
|
#include "core/hle/kernel/vm_manager.h"
|
||||||
#include "core/hle/service/plgldr/plgldr.h"
|
#include "core/hle/service/plgldr/plgldr.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "core/mmio.h"
|
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
@ -33,9 +32,6 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
|
||||||
backing_memory.GetPtr() + size != next.backing_memory.GetPtr()) {
|
backing_memory.GetPtr() + size != next.backing_memory.GetPtr()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (type == VMAType::MMIO && paddr + size != next.paddr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,26 +114,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, Memory
|
||||||
return MergeAdjacent(vma_handle);
|
return MergeAdjacent(vma_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size,
|
|
||||||
MemoryState state,
|
|
||||||
Memory::MMIORegionPointer mmio_handler) {
|
|
||||||
ASSERT(!is_locked);
|
|
||||||
|
|
||||||
// This is the appropriately sized VMA that will turn into our allocation.
|
|
||||||
CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
|
|
||||||
VirtualMemoryArea& final_vma = vma_handle->second;
|
|
||||||
ASSERT(final_vma.size == size);
|
|
||||||
|
|
||||||
final_vma.type = VMAType::MMIO;
|
|
||||||
final_vma.permissions = VMAPermission::ReadWrite;
|
|
||||||
final_vma.meminfo_state = state;
|
|
||||||
final_vma.paddr = paddr;
|
|
||||||
final_vma.mmio_handler = mmio_handler;
|
|
||||||
UpdatePageTableForVMA(final_vma);
|
|
||||||
|
|
||||||
return MergeAdjacent(vma_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultCode VMManager::ChangeMemoryState(VAddr target, u32 size, MemoryState expected_state,
|
ResultCode VMManager::ChangeMemoryState(VAddr target, u32 size, MemoryState expected_state,
|
||||||
VMAPermission expected_perms, MemoryState new_state,
|
VMAPermission expected_perms, MemoryState new_state,
|
||||||
VMAPermission new_perms) {
|
VMAPermission new_perms) {
|
||||||
|
@ -185,9 +161,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
|
||||||
vma.type = VMAType::Free;
|
vma.type = VMAType::Free;
|
||||||
vma.permissions = VMAPermission::None;
|
vma.permissions = VMAPermission::None;
|
||||||
vma.meminfo_state = MemoryState::Free;
|
vma.meminfo_state = MemoryState::Free;
|
||||||
|
|
||||||
vma.backing_memory = nullptr;
|
vma.backing_memory = nullptr;
|
||||||
vma.paddr = 0;
|
|
||||||
|
|
||||||
UpdatePageTableForVMA(vma);
|
UpdatePageTableForVMA(vma);
|
||||||
|
|
||||||
|
@ -344,9 +318,6 @@ VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u32 offset_in_vma) {
|
||||||
case VMAType::BackingMemory:
|
case VMAType::BackingMemory:
|
||||||
new_vma.backing_memory += offset_in_vma;
|
new_vma.backing_memory += offset_in_vma;
|
||||||
break;
|
break;
|
||||||
case VMAType::MMIO:
|
|
||||||
new_vma.paddr += offset_in_vma;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(old_vma.CanBeMergedWith(new_vma));
|
ASSERT(old_vma.CanBeMergedWith(new_vma));
|
||||||
|
@ -381,9 +352,6 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
|
||||||
case VMAType::BackingMemory:
|
case VMAType::BackingMemory:
|
||||||
memory.MapMemoryRegion(*page_table, vma.base, vma.size, vma.backing_memory);
|
memory.MapMemoryRegion(*page_table, vma.base, vma.size, vma.backing_memory);
|
||||||
break;
|
break;
|
||||||
case VMAType::MMIO:
|
|
||||||
memory.MapIoRegion(*page_table, vma.base, vma.size, vma.mmio_handler);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto plgldr = Service::PLGLDR::GetService(Core::System::GetInstance());
|
auto plgldr = Service::PLGLDR::GetService(Core::System::GetInstance());
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
#include <boost/serialization/map.hpp>
|
#include <boost/serialization/map.hpp>
|
||||||
#include <boost/serialization/shared_ptr.hpp>
|
#include <boost/serialization/shared_ptr.hpp>
|
||||||
#include <boost/serialization/split_member.hpp>
|
#include <boost/serialization/split_member.hpp>
|
||||||
|
@ -16,7 +14,6 @@
|
||||||
#include "core/hle/kernel/memory.h"
|
#include "core/hle/kernel/memory.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "core/mmio.h"
|
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
@ -25,8 +22,6 @@ enum class VMAType : u8 {
|
||||||
Free,
|
Free,
|
||||||
/// VMA is backed by a raw, unmanaged pointer.
|
/// VMA is backed by a raw, unmanaged pointer.
|
||||||
BackingMemory,
|
BackingMemory,
|
||||||
/// VMA is mapped to MMIO registers at a fixed PAddr.
|
|
||||||
MMIO,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Permissions for mapped memory blocks
|
/// Permissions for mapped memory blocks
|
||||||
|
@ -74,15 +69,10 @@ struct VirtualMemoryArea {
|
||||||
/// Tag returned by svcQueryMemory. Not otherwise used.
|
/// Tag returned by svcQueryMemory. Not otherwise used.
|
||||||
MemoryState meminfo_state = MemoryState::Free;
|
MemoryState meminfo_state = MemoryState::Free;
|
||||||
|
|
||||||
// Settings for type = BackingMemory
|
/// Settings for type = BackingMemory
|
||||||
/// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed.
|
/// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed.
|
||||||
MemoryRef backing_memory{};
|
MemoryRef backing_memory{};
|
||||||
|
|
||||||
// Settings for type = MMIO
|
|
||||||
/// Physical address of the register area this VMA maps to.
|
|
||||||
PAddr paddr = 0;
|
|
||||||
Memory::MMIORegionPointer mmio_handler = nullptr;
|
|
||||||
|
|
||||||
/// Tests if this area can be merged to the right with `next`.
|
/// Tests if this area can be merged to the right with `next`.
|
||||||
bool CanBeMergedWith(const VirtualMemoryArea& next) const;
|
bool CanBeMergedWith(const VirtualMemoryArea& next) const;
|
||||||
|
|
||||||
|
@ -96,8 +86,6 @@ private:
|
||||||
ar& permissions;
|
ar& permissions;
|
||||||
ar& meminfo_state;
|
ar& meminfo_state;
|
||||||
ar& backing_memory;
|
ar& backing_memory;
|
||||||
ar& paddr;
|
|
||||||
ar& mmio_handler;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -166,18 +154,6 @@ public:
|
||||||
ResultVal<VMAHandle> MapBackingMemory(VAddr target, MemoryRef memory, u32 size,
|
ResultVal<VMAHandle> MapBackingMemory(VAddr target, MemoryRef memory, u32 size,
|
||||||
MemoryState state);
|
MemoryState state);
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps a memory-mapped IO region at a given address.
|
|
||||||
*
|
|
||||||
* @param target The guest address to start the mapping at.
|
|
||||||
* @param paddr The physical address where the registers are present.
|
|
||||||
* @param size Size of the mapping.
|
|
||||||
* @param state MemoryState tag to attach to the VMA.
|
|
||||||
* @param mmio_handler The handler that will implement read and write for this MMIO region.
|
|
||||||
*/
|
|
||||||
ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state,
|
|
||||||
Memory::MMIORegionPointer mmio_handler);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the memory state and permissions of the specified range. The range's original memory
|
* Updates the memory state and permissions of the specified range. The range's original memory
|
||||||
* state and permissions must match the `expected` parameters.
|
* state and permissions must match the `expected` parameters.
|
||||||
|
|
|
@ -156,20 +156,6 @@ public:
|
||||||
return system.GetRunningCore().GetPC();
|
return system.GetRunningCore().GetPC();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This function should only be called for virtual addreses with attribute `PageType::Special`.
|
|
||||||
*/
|
|
||||||
MMIORegionPointer GetMMIOHandler(const PageTable& page_table, VAddr vaddr) {
|
|
||||||
for (const auto& region : page_table.special_regions) {
|
|
||||||
if (vaddr >= region.base && vaddr < (region.base + region.size)) {
|
|
||||||
return region.handler;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_MSG(false, "Mapped IO page without a handler @ {:08X}", vaddr);
|
|
||||||
return nullptr; // Should never happen
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool UNSAFE>
|
template <bool UNSAFE>
|
||||||
void ReadBlockImpl(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
|
void ReadBlockImpl(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
|
||||||
const std::size_t size) {
|
const std::size_t size) {
|
||||||
|
@ -201,12 +187,6 @@ public:
|
||||||
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PageType::Special: {
|
|
||||||
MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
|
|
||||||
DEBUG_ASSERT(handler);
|
|
||||||
handler->ReadBlock(current_vaddr, dest_buffer, copy_amount);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PageType::RasterizerCachedMemory: {
|
case PageType::RasterizerCachedMemory: {
|
||||||
if constexpr (!UNSAFE) {
|
if constexpr (!UNSAFE) {
|
||||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||||
|
@ -255,12 +235,6 @@ public:
|
||||||
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PageType::Special: {
|
|
||||||
MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
|
|
||||||
DEBUG_ASSERT(handler);
|
|
||||||
handler->WriteBlock(current_vaddr, src_buffer, copy_amount);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PageType::RasterizerCachedMemory: {
|
case PageType::RasterizerCachedMemory: {
|
||||||
if constexpr (!UNSAFE) {
|
if constexpr (!UNSAFE) {
|
||||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||||
|
@ -405,16 +379,6 @@ void MemorySystem::MapMemoryRegion(PageTable& page_table, VAddr base, u32 size,
|
||||||
MapPages(page_table, base / CITRA_PAGE_SIZE, size / CITRA_PAGE_SIZE, target, PageType::Memory);
|
MapPages(page_table, base / CITRA_PAGE_SIZE, size / CITRA_PAGE_SIZE, target, PageType::Memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemorySystem::MapIoRegion(PageTable& page_table, VAddr base, u32 size,
|
|
||||||
MMIORegionPointer mmio_handler) {
|
|
||||||
ASSERT_MSG((size & CITRA_PAGE_MASK) == 0, "non-page aligned size: {:08X}", size);
|
|
||||||
ASSERT_MSG((base & CITRA_PAGE_MASK) == 0, "non-page aligned base: {:08X}", base);
|
|
||||||
MapPages(page_table, base / CITRA_PAGE_SIZE, size / CITRA_PAGE_SIZE, nullptr,
|
|
||||||
PageType::Special);
|
|
||||||
|
|
||||||
page_table.special_regions.emplace_back(SpecialRegion{base, size, mmio_handler});
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemorySystem::UnmapRegion(PageTable& page_table, VAddr base, u32 size) {
|
void MemorySystem::UnmapRegion(PageTable& page_table, VAddr base, u32 size) {
|
||||||
ASSERT_MSG((size & CITRA_PAGE_MASK) == 0, "non-page aligned size: {:08X}", size);
|
ASSERT_MSG((size & CITRA_PAGE_MASK) == 0, "non-page aligned size: {:08X}", size);
|
||||||
ASSERT_MSG((base & CITRA_PAGE_MASK) == 0, "non-page aligned base: {:08X}", base);
|
ASSERT_MSG((base & CITRA_PAGE_MASK) == 0, "non-page aligned base: {:08X}", base);
|
||||||
|
@ -437,9 +401,6 @@ void MemorySystem::UnregisterPageTable(std::shared_ptr<PageTable> page_table) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr);
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T MemorySystem::Read(const VAddr vaddr) {
|
T MemorySystem::Read(const VAddr vaddr) {
|
||||||
const u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
const u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
||||||
|
@ -482,8 +443,6 @@ T MemorySystem::Read(const VAddr vaddr) {
|
||||||
std::memcpy(&value, GetPointerForRasterizerCache(vaddr), sizeof(T));
|
std::memcpy(&value, GetPointerForRasterizerCache(vaddr), sizeof(T));
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
case PageType::Special:
|
|
||||||
return ReadMMIO<T>(impl->GetMMIOHandler(*impl->current_page_table, vaddr), vaddr);
|
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
@ -491,9 +450,6 @@ T MemorySystem::Read(const VAddr vaddr) {
|
||||||
return T{};
|
return T{};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void WriteMMIO(MMIORegionPointer mmio_handler, VAddr addr, const T data);
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void MemorySystem::Write(const VAddr vaddr, const T data) {
|
void MemorySystem::Write(const VAddr vaddr, const T data) {
|
||||||
u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
||||||
|
@ -531,9 +487,6 @@ void MemorySystem::Write(const VAddr vaddr, const T data) {
|
||||||
std::memcpy(GetPointerForRasterizerCache(vaddr), &data, sizeof(T));
|
std::memcpy(GetPointerForRasterizerCache(vaddr), &data, sizeof(T));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PageType::Special:
|
|
||||||
WriteMMIO<T>(impl->GetMMIOHandler(*impl->current_page_table, vaddr), vaddr, data);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
@ -564,9 +517,6 @@ bool MemorySystem::WriteExclusive(const VAddr vaddr, const T data, const T expec
|
||||||
reinterpret_cast<volatile T*>(GetPointerForRasterizerCache(vaddr).GetPtr());
|
reinterpret_cast<volatile T*>(GetPointerForRasterizerCache(vaddr).GetPtr());
|
||||||
return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
|
return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
|
||||||
}
|
}
|
||||||
case PageType::Special:
|
|
||||||
WriteMMIO<T>(impl->GetMMIOHandler(*impl->current_page_table, vaddr), vaddr, data);
|
|
||||||
return false;
|
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
@ -577,18 +527,12 @@ bool MemorySystem::IsValidVirtualAddress(const Kernel::Process& process, const V
|
||||||
auto& page_table = *process.vm_manager.page_table;
|
auto& page_table = *process.vm_manager.page_table;
|
||||||
|
|
||||||
auto page_pointer = page_table.pointers[vaddr >> CITRA_PAGE_BITS];
|
auto page_pointer = page_table.pointers[vaddr >> CITRA_PAGE_BITS];
|
||||||
if (page_pointer)
|
if (page_pointer) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (page_table.attributes[vaddr >> CITRA_PAGE_BITS] == PageType::RasterizerCachedMemory)
|
if (page_table.attributes[vaddr >> CITRA_PAGE_BITS] == PageType::RasterizerCachedMemory) {
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (page_table.attributes[vaddr >> CITRA_PAGE_BITS] != PageType::Special)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
MMIORegionPointer mmio_region = impl->GetMMIOHandler(page_table, vaddr);
|
|
||||||
if (mmio_region) {
|
|
||||||
return mmio_region->IsValidAddress(vaddr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -923,8 +867,6 @@ void MemorySystem::ZeroBlock(const Kernel::Process& process, const VAddr dest_ad
|
||||||
std::size_t page_index = dest_addr >> CITRA_PAGE_BITS;
|
std::size_t page_index = dest_addr >> CITRA_PAGE_BITS;
|
||||||
std::size_t page_offset = dest_addr & CITRA_PAGE_MASK;
|
std::size_t page_offset = dest_addr & CITRA_PAGE_MASK;
|
||||||
|
|
||||||
static const std::array<u8, CITRA_PAGE_SIZE> zeros = {};
|
|
||||||
|
|
||||||
while (remaining_size > 0) {
|
while (remaining_size > 0) {
|
||||||
const std::size_t copy_amount = std::min(CITRA_PAGE_SIZE - page_offset, remaining_size);
|
const std::size_t copy_amount = std::min(CITRA_PAGE_SIZE - page_offset, remaining_size);
|
||||||
const VAddr current_vaddr =
|
const VAddr current_vaddr =
|
||||||
|
@ -945,12 +887,6 @@ void MemorySystem::ZeroBlock(const Kernel::Process& process, const VAddr dest_ad
|
||||||
std::memset(dest_ptr, 0, copy_amount);
|
std::memset(dest_ptr, 0, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PageType::Special: {
|
|
||||||
MMIORegionPointer handler = impl->GetMMIOHandler(page_table, current_vaddr);
|
|
||||||
DEBUG_ASSERT(handler);
|
|
||||||
handler->WriteBlock(current_vaddr, zeros.data(), copy_amount);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PageType::RasterizerCachedMemory: {
|
case PageType::RasterizerCachedMemory: {
|
||||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||||
FlushMode::Invalidate);
|
FlushMode::Invalidate);
|
||||||
|
@ -1000,14 +936,6 @@ void MemorySystem::CopyBlock(const Kernel::Process& dest_process,
|
||||||
WriteBlock(dest_process, dest_addr, src_ptr, copy_amount);
|
WriteBlock(dest_process, dest_addr, src_ptr, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PageType::Special: {
|
|
||||||
MMIORegionPointer handler = impl->GetMMIOHandler(page_table, current_vaddr);
|
|
||||||
DEBUG_ASSERT(handler);
|
|
||||||
std::vector<u8> buffer(copy_amount);
|
|
||||||
handler->ReadBlock(current_vaddr, buffer.data(), buffer.size());
|
|
||||||
WriteBlock(dest_process, dest_addr, buffer.data(), buffer.size());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PageType::RasterizerCachedMemory: {
|
case PageType::RasterizerCachedMemory: {
|
||||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||||
FlushMode::Flush);
|
FlushMode::Flush);
|
||||||
|
@ -1027,46 +955,6 @@ void MemorySystem::CopyBlock(const Kernel::Process& dest_process,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
|
||||||
u8 ReadMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr) {
|
|
||||||
return mmio_handler->Read8(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
u16 ReadMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr) {
|
|
||||||
return mmio_handler->Read16(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
u32 ReadMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr) {
|
|
||||||
return mmio_handler->Read32(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
u64 ReadMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr) {
|
|
||||||
return mmio_handler->Read64(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void WriteMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr, const u8 data) {
|
|
||||||
mmio_handler->Write8(addr, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void WriteMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr, const u16 data) {
|
|
||||||
mmio_handler->Write16(addr, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void WriteMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr, const u32 data) {
|
|
||||||
mmio_handler->Write32(addr, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void WriteMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr, const u64 data) {
|
|
||||||
mmio_handler->Write64(addr, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 MemorySystem::GetFCRAMOffset(const u8* pointer) const {
|
u32 MemorySystem::GetFCRAMOffset(const u8* pointer) const {
|
||||||
ASSERT(pointer >= impl->fcram.get() && pointer <= impl->fcram.get() + Memory::FCRAM_N3DS_SIZE);
|
ASSERT(pointer >= impl->fcram.get() && pointer <= impl->fcram.get() + Memory::FCRAM_N3DS_SIZE);
|
||||||
return static_cast<u32>(pointer - impl->fcram.get());
|
return static_cast<u32>(pointer - impl->fcram.get());
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
#include <boost/serialization/vector.hpp>
|
#include <boost/serialization/vector.hpp>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/memory_ref.h"
|
#include "common/memory_ref.h"
|
||||||
#include "core/mmio.h"
|
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
class Process;
|
class Process;
|
||||||
|
@ -43,23 +42,6 @@ enum class PageType {
|
||||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||||
/// invalidation
|
/// invalidation
|
||||||
RasterizerCachedMemory,
|
RasterizerCachedMemory,
|
||||||
/// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
|
|
||||||
Special,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SpecialRegion {
|
|
||||||
VAddr base;
|
|
||||||
u32 size;
|
|
||||||
MMIORegionPointer handler;
|
|
||||||
|
|
||||||
private:
|
|
||||||
template <class Archive>
|
|
||||||
void serialize(Archive& ar, const unsigned int file_version) {
|
|
||||||
ar& base;
|
|
||||||
ar& size;
|
|
||||||
ar& handler;
|
|
||||||
}
|
|
||||||
friend class boost::serialization::access;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,12 +90,6 @@ struct PageTable {
|
||||||
|
|
||||||
Pointers pointers;
|
Pointers pointers;
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
|
|
||||||
* type `Special`.
|
|
||||||
*/
|
|
||||||
std::vector<SpecialRegion> special_regions;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Array of fine grained page attributes. If it is set to any value other than `Memory`, then
|
* Array of fine grained page attributes. If it is set to any value other than `Memory`, then
|
||||||
* the corresponding entry in `pointers` MUST be set to null.
|
* the corresponding entry in `pointers` MUST be set to null.
|
||||||
|
@ -130,7 +106,6 @@ private:
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive& ar, const unsigned int) {
|
void serialize(Archive& ar, const unsigned int) {
|
||||||
ar& pointers.refs;
|
ar& pointers.refs;
|
||||||
ar& special_regions;
|
|
||||||
ar& attributes;
|
ar& attributes;
|
||||||
for (std::size_t i = 0; i < PAGE_TABLE_NUM_ENTRIES; i++) {
|
for (std::size_t i = 0; i < PAGE_TABLE_NUM_ENTRIES; i++) {
|
||||||
pointers.raw[i] = pointers.refs[i].GetPtr();
|
pointers.raw[i] = pointers.refs[i].GetPtr();
|
||||||
|
@ -302,15 +277,6 @@ public:
|
||||||
*/
|
*/
|
||||||
void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, MemoryRef target);
|
void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, MemoryRef target);
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps a region of the emulated process address space as a IO region.
|
|
||||||
* @param page_table The page table of the emulated process.
|
|
||||||
* @param base The address to start mapping at. Must be page-aligned.
|
|
||||||
* @param size The amount of bytes to map. Must be page-aligned.
|
|
||||||
* @param mmio_handler The handler that backs the mapping.
|
|
||||||
*/
|
|
||||||
void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler);
|
|
||||||
|
|
||||||
void UnmapRegion(PageTable& page_table, VAddr base, u32 size);
|
void UnmapRegion(PageTable& page_table, VAddr base, u32 size);
|
||||||
|
|
||||||
/// Currently active page table
|
/// Currently active page table
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
// Copyright 2016 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
namespace Memory {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a device with memory mapped IO.
|
|
||||||
* A device may be mapped to multiple regions of memory.
|
|
||||||
*/
|
|
||||||
class MMIORegion {
|
|
||||||
public:
|
|
||||||
virtual ~MMIORegion() = default;
|
|
||||||
|
|
||||||
virtual bool IsValidAddress(VAddr addr) = 0;
|
|
||||||
|
|
||||||
virtual u8 Read8(VAddr addr) = 0;
|
|
||||||
virtual u16 Read16(VAddr addr) = 0;
|
|
||||||
virtual u32 Read32(VAddr addr) = 0;
|
|
||||||
virtual u64 Read64(VAddr addr) = 0;
|
|
||||||
|
|
||||||
virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0;
|
|
||||||
|
|
||||||
virtual void Write8(VAddr addr, u8 data) = 0;
|
|
||||||
virtual void Write16(VAddr addr, u16 data) = 0;
|
|
||||||
virtual void Write32(VAddr addr, u32 data) = 0;
|
|
||||||
virtual void Write64(VAddr addr, u64 data) = 0;
|
|
||||||
|
|
||||||
virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class boost::serialization::access;
|
|
||||||
template <class Archive>
|
|
||||||
void serialize(Archive& ar, const unsigned int file_version) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
using MMIORegionPointer = std::shared_ptr<MMIORegion>;
|
|
||||||
}; // namespace Memory
|
|
|
@ -2,9 +2,6 @@ add_executable(tests
|
||||||
common/bit_field.cpp
|
common/bit_field.cpp
|
||||||
common/file_util.cpp
|
common/file_util.cpp
|
||||||
common/param_package.cpp
|
common/param_package.cpp
|
||||||
core/arm/arm_test_common.cpp
|
|
||||||
core/arm/arm_test_common.h
|
|
||||||
core/arm/dyncom/arm_dyncom_vfp_tests.cpp
|
|
||||||
core/core_timing.cpp
|
core/core_timing.cpp
|
||||||
core/file_sys/path_parser.cpp
|
core/file_sys/path_parser.cpp
|
||||||
core/hle/kernel/hle_ipc.cpp
|
core/hle/kernel/hle_ipc.cpp
|
||||||
|
|
|
@ -1,143 +0,0 @@
|
||||||
// Copyright 2016 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "core/core.h"
|
|
||||||
#include "core/core_timing.h"
|
|
||||||
#include "core/hle/kernel/process.h"
|
|
||||||
#include "core/memory.h"
|
|
||||||
#include "tests/core/arm/arm_test_common.h"
|
|
||||||
|
|
||||||
namespace ArmTests {
|
|
||||||
|
|
||||||
static std::shared_ptr<Memory::PageTable> page_table = nullptr;
|
|
||||||
|
|
||||||
TestEnvironment::TestEnvironment(bool mutable_memory_)
|
|
||||||
: mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) {
|
|
||||||
|
|
||||||
timing = std::make_unique<Core::Timing>(1, 100);
|
|
||||||
system = std::make_unique<Core::System>();
|
|
||||||
memory = std::make_unique<Memory::MemorySystem>(*system);
|
|
||||||
kernel = std::make_unique<Kernel::KernelSystem>(
|
|
||||||
*memory, *timing, [] {}, Kernel::MemoryMode::Prod, 1,
|
|
||||||
Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy});
|
|
||||||
|
|
||||||
kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0)));
|
|
||||||
page_table = kernel->GetCurrentProcess()->vm_manager.page_table;
|
|
||||||
|
|
||||||
page_table->Clear();
|
|
||||||
|
|
||||||
memory->MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
|
|
||||||
memory->MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);
|
|
||||||
|
|
||||||
kernel->SetCurrentMemoryPageTable(page_table);
|
|
||||||
}
|
|
||||||
|
|
||||||
TestEnvironment::~TestEnvironment() {
|
|
||||||
memory->UnmapRegion(*page_table, 0x80000000, 0x80000000);
|
|
||||||
memory->UnmapRegion(*page_table, 0x00000000, 0x80000000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) {
|
|
||||||
SetMemory32(vaddr + 0, static_cast<u32>(value));
|
|
||||||
SetMemory32(vaddr + 4, static_cast<u32>(value >> 32));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestEnvironment::SetMemory32(VAddr vaddr, u32 value) {
|
|
||||||
SetMemory16(vaddr + 0, static_cast<u16>(value));
|
|
||||||
SetMemory16(vaddr + 2, static_cast<u16>(value >> 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestEnvironment::SetMemory16(VAddr vaddr, u16 value) {
|
|
||||||
SetMemory8(vaddr + 0, static_cast<u8>(value));
|
|
||||||
SetMemory8(vaddr + 1, static_cast<u8>(value >> 8));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestEnvironment::SetMemory8(VAddr vaddr, u8 value) {
|
|
||||||
test_memory->data[vaddr] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<WriteRecord> TestEnvironment::GetWriteRecords() const {
|
|
||||||
return write_records;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestEnvironment::ClearWriteRecords() {
|
|
||||||
write_records.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
TestEnvironment::TestMemory::~TestMemory() {}
|
|
||||||
|
|
||||||
bool TestEnvironment::TestMemory::IsValidAddress(VAddr addr) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 TestEnvironment::TestMemory::Read8(VAddr addr) {
|
|
||||||
auto iter = data.find(addr);
|
|
||||||
if (iter == data.end()) {
|
|
||||||
return addr; // Some arbitrary data
|
|
||||||
}
|
|
||||||
return iter->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
u16 TestEnvironment::TestMemory::Read16(VAddr addr) {
|
|
||||||
return Read8(addr) | static_cast<u16>(Read8(addr + 1)) << 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 TestEnvironment::TestMemory::Read32(VAddr addr) {
|
|
||||||
return Read16(addr) | static_cast<u32>(Read16(addr + 2)) << 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 TestEnvironment::TestMemory::Read64(VAddr addr) {
|
|
||||||
return Read32(addr) | static_cast<u64>(Read32(addr + 4)) << 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TestEnvironment::TestMemory::ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) {
|
|
||||||
VAddr addr = src_addr;
|
|
||||||
u8* data = static_cast<u8*>(dest_buffer);
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < size; i++, addr++, data++) {
|
|
||||||
*data = Read8(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestEnvironment::TestMemory::Write8(VAddr addr, u8 data) {
|
|
||||||
env->write_records.emplace_back(8, addr, data);
|
|
||||||
if (env->mutable_memory)
|
|
||||||
env->SetMemory8(addr, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestEnvironment::TestMemory::Write16(VAddr addr, u16 data) {
|
|
||||||
env->write_records.emplace_back(16, addr, data);
|
|
||||||
if (env->mutable_memory)
|
|
||||||
env->SetMemory16(addr, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestEnvironment::TestMemory::Write32(VAddr addr, u32 data) {
|
|
||||||
env->write_records.emplace_back(32, addr, data);
|
|
||||||
if (env->mutable_memory)
|
|
||||||
env->SetMemory32(addr, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestEnvironment::TestMemory::Write64(VAddr addr, u64 data) {
|
|
||||||
env->write_records.emplace_back(64, addr, data);
|
|
||||||
if (env->mutable_memory)
|
|
||||||
env->SetMemory64(addr, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TestEnvironment::TestMemory::WriteBlock(VAddr dest_addr, const void* src_buffer,
|
|
||||||
std::size_t size) {
|
|
||||||
VAddr addr = dest_addr;
|
|
||||||
const u8* data = static_cast<const u8*>(src_buffer);
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < size; i++, addr++, data++) {
|
|
||||||
env->write_records.emplace_back(8, addr, *data);
|
|
||||||
if (env->mutable_memory)
|
|
||||||
env->SetMemory8(addr, *data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ArmTests
|
|
|
@ -1,93 +0,0 @@
|
||||||
// Copyright 2016 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include <tuple>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "core/hle/kernel/kernel.h"
|
|
||||||
#include "core/mmio.h"
|
|
||||||
|
|
||||||
namespace ArmTests {
|
|
||||||
|
|
||||||
struct WriteRecord {
|
|
||||||
WriteRecord(std::size_t size, VAddr addr, u64 data) : size(size), addr(addr), data(data) {}
|
|
||||||
std::size_t size;
|
|
||||||
VAddr addr;
|
|
||||||
u64 data;
|
|
||||||
bool operator==(const WriteRecord& o) const {
|
|
||||||
return std::tie(size, addr, data) == std::tie(o.size, o.addr, o.data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TestEnvironment final {
|
|
||||||
public:
|
|
||||||
/*
|
|
||||||
* Inititalise test environment
|
|
||||||
* @param mutable_memory If false, writes to memory can never be read back.
|
|
||||||
* (Memory is immutable.)
|
|
||||||
*/
|
|
||||||
explicit TestEnvironment(bool mutable_memory = false);
|
|
||||||
|
|
||||||
/// Shutdown test environment
|
|
||||||
~TestEnvironment();
|
|
||||||
|
|
||||||
/// Sets value at memory location vaddr.
|
|
||||||
void SetMemory8(VAddr vaddr, u8 value);
|
|
||||||
void SetMemory16(VAddr vaddr, u16 value);
|
|
||||||
void SetMemory32(VAddr vaddr, u32 value);
|
|
||||||
void SetMemory64(VAddr vaddr, u64 value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whenever Memory::Write{8,16,32,64} is called within the test environment,
|
|
||||||
* a new write-record is made.
|
|
||||||
* @returns A vector of write records made since they were last cleared.
|
|
||||||
*/
|
|
||||||
std::vector<WriteRecord> GetWriteRecords() const;
|
|
||||||
|
|
||||||
/// Empties the internal write-record store.
|
|
||||||
void ClearWriteRecords();
|
|
||||||
|
|
||||||
Memory::MemorySystem& GetMemory() {
|
|
||||||
return *memory;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend struct TestMemory;
|
|
||||||
struct TestMemory final : Memory::MMIORegion {
|
|
||||||
explicit TestMemory(TestEnvironment* env_) : env(env_) {}
|
|
||||||
TestEnvironment* env;
|
|
||||||
|
|
||||||
~TestMemory() override;
|
|
||||||
|
|
||||||
bool IsValidAddress(VAddr addr) override;
|
|
||||||
|
|
||||||
u8 Read8(VAddr addr) override;
|
|
||||||
u16 Read16(VAddr addr) override;
|
|
||||||
u32 Read32(VAddr addr) override;
|
|
||||||
u64 Read64(VAddr addr) override;
|
|
||||||
|
|
||||||
bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) override;
|
|
||||||
|
|
||||||
void Write8(VAddr addr, u8 data) override;
|
|
||||||
void Write16(VAddr addr, u16 data) override;
|
|
||||||
void Write32(VAddr addr, u32 data) override;
|
|
||||||
void Write64(VAddr addr, u64 data) override;
|
|
||||||
|
|
||||||
bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) override;
|
|
||||||
|
|
||||||
std::unordered_map<VAddr, u8> data;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool mutable_memory;
|
|
||||||
std::shared_ptr<TestMemory> test_memory;
|
|
||||||
std::vector<WriteRecord> write_records;
|
|
||||||
|
|
||||||
std::unique_ptr<Core::Timing> timing;
|
|
||||||
std::unique_ptr<Core::System> system;
|
|
||||||
std::unique_ptr<Memory::MemorySystem> memory;
|
|
||||||
std::unique_ptr<Kernel::KernelSystem> kernel;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ArmTests
|
|
|
@ -1,51 +0,0 @@
|
||||||
// Copyright 2016 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include <catch2/catch_test_macros.hpp>
|
|
||||||
#include "core/arm/dyncom/arm_dyncom.h"
|
|
||||||
#include "core/core.h"
|
|
||||||
#include "core/core_timing.h"
|
|
||||||
#include "tests/core/arm/arm_test_common.h"
|
|
||||||
|
|
||||||
namespace ArmTests {
|
|
||||||
|
|
||||||
struct VfpTestCase {
|
|
||||||
u32 initial_fpscr;
|
|
||||||
u32 a;
|
|
||||||
u32 b;
|
|
||||||
u32 result;
|
|
||||||
u32 final_fpscr;
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_CASE("ARM_DynCom (vfp): vadd", "[arm_dyncom]") {
|
|
||||||
TestEnvironment test_env(false);
|
|
||||||
test_env.SetMemory32(0, 0xEE321A03); // vadd.f32 s2, s4, s6
|
|
||||||
test_env.SetMemory32(4, 0xEAFFFFFE); // b +#0
|
|
||||||
|
|
||||||
Core::System system;
|
|
||||||
Core::ARM_DynCom dyncom(system, test_env.GetMemory(), USER32MODE, 0, nullptr);
|
|
||||||
|
|
||||||
std::vector<VfpTestCase> test_cases{{
|
|
||||||
#include "vfp_vadd_f32.inc"
|
|
||||||
}};
|
|
||||||
|
|
||||||
for (const auto& test_case : test_cases) {
|
|
||||||
dyncom.SetPC(0);
|
|
||||||
dyncom.SetVFPSystemReg(VFP_FPSCR, test_case.initial_fpscr);
|
|
||||||
dyncom.SetVFPReg(4, test_case.a);
|
|
||||||
dyncom.SetVFPReg(6, test_case.b);
|
|
||||||
dyncom.Step();
|
|
||||||
if (dyncom.GetVFPReg(2) != test_case.result ||
|
|
||||||
dyncom.GetVFPSystemReg(VFP_FPSCR) != test_case.final_fpscr) {
|
|
||||||
printf("f: %x\n", test_case.initial_fpscr);
|
|
||||||
printf("a: %x\n", test_case.a);
|
|
||||||
printf("b: %x\n", test_case.b);
|
|
||||||
printf("c: %x (%x)\n", dyncom.GetVFPReg(2), test_case.result);
|
|
||||||
printf("f: %x (%x)\n", dyncom.GetVFPSystemReg(VFP_FPSCR), test_case.final_fpscr);
|
|
||||||
FAIL();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ArmTests
|
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue