diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 504b8ada47..79f2491d49 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -107,6 +107,7 @@ set(SRCS ActionReplay.cpp HW/Memmap.cpp HW/MemmapFunctions.cpp HW/MemoryInterface.cpp + HW/MMIO.cpp HW/ProcessorInterface.cpp HW/SI.cpp HW/SI_DeviceAMBaseboard.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 69b9bda339..fdf6fc4b53 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -145,6 +145,7 @@ + @@ -342,6 +343,8 @@ + + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 40c890398e..99626fecb4 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -510,6 +510,9 @@ HW %28Flipper/Hollywood%29 + + HW %28Flipper/Hollywood%29 + HW %28Flipper/Hollywood%29 @@ -1037,6 +1040,12 @@ HW %28Flipper/Hollywood%29 + + HW %28Flipper/Hollywood%29 + + + HW %28Flipper/Hollywood%29 + HW %28Flipper/Hollywood%29 @@ -1219,4 +1228,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Core/HW/MMIO.cpp b/Source/Core/Core/HW/MMIO.cpp new file mode 100644 index 0000000000..759823f480 --- /dev/null +++ b/Source/Core/Core/HW/MMIO.cpp @@ -0,0 +1,383 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "MMIO.h" +#include "MMIOHandlers.h" + +#include + +namespace MMIO +{ + +// Base classes for the two handling method hierarchies. Note that a single +// class can inherit from both. +// +// At the moment the only common element between all the handling method is +// that they should be able to accept a visitor of the appropriate type. +template +class ReadHandlingMethod +{ +public: + virtual ~ReadHandlingMethod() {} + virtual void AcceptReadVisitor(ReadHandlingMethodVisitor& v) const = 0; +}; +template +class WriteHandlingMethod +{ +public: + virtual ~WriteHandlingMethod() {} + virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor& v) const = 0; +}; + +// Constant: handling method holds a single integer and passes it to the +// visitor. This is a read only handling method: storing to a constant does not +// mean anything. +template +class ConstantHandlingMethod : public ReadHandlingMethod +{ +public: + explicit ConstantHandlingMethod(T value) : value_(value) + { + } + + virtual ~ConstantHandlingMethod() {} + + virtual void AcceptReadVisitor(ReadHandlingMethodVisitor& v) const + { + v.VisitConstant(value_); + } + +private: + T value_; +}; +template +ReadHandlingMethod* Constant(T value) +{ + return new ConstantHandlingMethod(value); +} + +// Nop: extremely simple write handling method that does nothing at all, only +// respond to visitors and dispatch to the correct method. This is write only +// since reads should always at least return a value. +template +class NopHandlingMethod : public WriteHandlingMethod +{ +public: + NopHandlingMethod() {} + virtual ~NopHandlingMethod() {} + virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor& v) const + { + v.VisitNop(); + } +}; +template +WriteHandlingMethod* Nop() +{ + return new NopHandlingMethod(); +} + +// Direct: handling method holds a pointer to the value where to read/write the +// data from, as well as a mask that is used to restrict reading/writing only +// to a given set of bits. +template +class DirectHandlingMethod : public ReadHandlingMethod, + public WriteHandlingMethod +{ +public: + DirectHandlingMethod(T* addr, u32 mask) : addr_(addr), mask_(mask) + { + } + + virtual ~DirectHandlingMethod() {} + + virtual void AcceptReadVisitor(ReadHandlingMethodVisitor& v) const + { + v.VisitDirect(addr_, mask_); + } + + virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor& v) const + { + v.VisitDirect(addr_, mask_); + } + +private: + T* addr_; + u32 mask_; +}; +template +ReadHandlingMethod* DirectRead(const T* addr, u32 mask) +{ + return new DirectHandlingMethod(const_cast(addr), mask); +} +template +ReadHandlingMethod* DirectRead(volatile const T* addr, u32 mask) +{ + return new DirectHandlingMethod((T*)addr, mask); +} +template +WriteHandlingMethod* DirectWrite(T* addr, u32 mask) +{ + return new DirectHandlingMethod(addr, mask); +} +template +WriteHandlingMethod* DirectWrite(volatile T* addr, u32 mask) +{ + return new DirectHandlingMethod((T*)addr, mask); +} + +// Complex: holds a lambda that is called when a read or a write is executed. +// This gives complete control to the user as to what is going to happen during +// that read or write, but reduces the optimization potential. +template +class ComplexHandlingMethod : public ReadHandlingMethod, + public WriteHandlingMethod +{ +public: + explicit ComplexHandlingMethod(std::function read_lambda) + : read_lambda_(read_lambda), write_lambda_(InvalidWriteLambda()) + { + } + + explicit ComplexHandlingMethod(std::function write_lambda) + : read_lambda_(InvalidReadLambda()), write_lambda_(write_lambda) + { + } + + virtual ~ComplexHandlingMethod() {} + + virtual void AcceptReadVisitor(ReadHandlingMethodVisitor& v) const + { + v.VisitComplex(read_lambda_); + } + + virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor& v) const + { + v.VisitComplex(write_lambda_); + } + +private: + std::function InvalidReadLambda() const + { + return [](u32) { + _dbg_assert_msg_(MEMMAP, 0, "Called the read lambda on a write " + "complex handler."); + return 0; + }; + } + + std::function InvalidWriteLambda() const + { + return [](u32, T) { + _dbg_assert_msg_(MEMMAP, 0, "Called the write lambda on a read " + "complex handler."); + }; + } + + std::function read_lambda_; + std::function write_lambda_; +}; +template +ReadHandlingMethod* ComplexRead(std::function lambda) +{ + return new ComplexHandlingMethod(lambda); +} +template +WriteHandlingMethod* ComplexWrite(std::function lambda) +{ + return new ComplexHandlingMethod(lambda); +} + +// Invalid: specialization of the complex handling type with lambdas that +// display error messages. +template +ReadHandlingMethod* InvalidRead() +{ + return ComplexRead([](u32 addr) { + ERROR_LOG(MEMMAP, "Trying to read from an invalid MMIO (addr=%08x)", + addr); + return -1; + }); +} +template +WriteHandlingMethod* InvalidWrite() +{ + return ComplexWrite([](u32 addr, T val) { + ERROR_LOG(MEMMAP, "Trying to write to an invalid MMIO (addr=%08x, val=%08x)", + addr, (u32)val); + }); +} + +// Converters to larger and smaller size. Probably the most complex of these +// handlers to implement. They do not define new handling method types but +// instead will internally use the types defined above. +template struct SmallerAccessSize {}; +template <> struct SmallerAccessSize { typedef u8 value; }; +template <> struct SmallerAccessSize { typedef u16 value; }; + +template struct LargerAccessSize {}; +template <> struct LargerAccessSize { typedef u16 value; }; +template <> struct LargerAccessSize { typedef u32 value; }; + +template +ReadHandlingMethod* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr) +{ + typedef typename SmallerAccessSize::value ST; + + const ReadHandler* high_part; + const ReadHandler* low_part; + mmio->GetHandlerForRead(high_part_addr, &high_part); + mmio->GetHandlerForRead(low_part_addr, &low_part); + + // TODO(delroth): optimize + return ComplexRead([high_part, low_part](u32 addr) { + return ((T)high_part->Read(addr) << (8 * sizeof (ST))) | low_part->Read(addr); + }); +} + +template +WriteHandlingMethod* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr) +{ + typedef typename SmallerAccessSize::value ST; + + const WriteHandler* high_part; + const WriteHandler* low_part; + mmio->GetHandlerForWrite(high_part_addr, &high_part); + mmio->GetHandlerForWrite(low_part_addr, &low_part); + + // TODO(delroth): optimize + return ComplexWrite([high_part, low_part](u32 addr, T val) { + high_part->Write(addr, val >> (8 * sizeof (ST))); + low_part->Write(addr, (ST)val); + }); +} + +template +ReadHandlingMethod* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift) +{ + typedef typename LargerAccessSize::value LT; + + const ReadHandler* large; + mmio->GetHandlerForRead(larger_addr, &large); + + // TODO(delroth): optimize + return ComplexRead([large, shift](u32 addr) { + return large->Read(addr & ~(sizeof (LT) - 1)) >> shift; + }); +} + +// Inplementation of the ReadHandler and WriteHandler class. There is a lot of +// redundant code between these two classes but trying to abstract it away +// brings more trouble than it fixes. +template +ReadHandler::ReadHandler() : m_Method(nullptr) +{ + ResetMethod(InvalidRead()); +} + +template +ReadHandler::ReadHandler(ReadHandlingMethod* method) + : m_Method(nullptr) +{ + ResetMethod(method); +} + +template +ReadHandler::~ReadHandler() +{ +} + +template +void ReadHandler::Visit(ReadHandlingMethodVisitor& visitor) const +{ + m_Method->AcceptReadVisitor(visitor); +} + +template +void ReadHandler::ResetMethod(ReadHandlingMethod* method) +{ + m_Method.reset(method); + + struct FuncCreatorVisitor : public ReadHandlingMethodVisitor + { + std::function ret; + + virtual void VisitConstant(T value) + { + ret = [value](u32) { return value; }; + } + + virtual void VisitDirect(const T* addr, u32 mask) + { + ret = [addr, mask](u32) { return *addr & mask; }; + } + + virtual void VisitComplex(std::function lambda) + { + ret = lambda; + } + }; + + FuncCreatorVisitor v; + Visit(v); + m_ReadFunc = v.ret; +} + +template +WriteHandler::WriteHandler() : m_Method(nullptr) +{ + ResetMethod(InvalidWrite()); +} + +template +WriteHandler::WriteHandler(WriteHandlingMethod* method) + : m_Method(nullptr) +{ + ResetMethod(method); +} + +template +WriteHandler::~WriteHandler() +{ +} + +template +void WriteHandler::Visit(WriteHandlingMethodVisitor& visitor) const +{ + m_Method->AcceptWriteVisitor(visitor); +} + +template +void WriteHandler::ResetMethod(WriteHandlingMethod* method) +{ + m_Method.reset(method); + + struct FuncCreatorVisitor : public WriteHandlingMethodVisitor + { + std::function ret; + + virtual void VisitNop() + { + ret = [](u32, T) {}; + } + + virtual void VisitDirect(T* ptr, u32 mask) + { + ret = [ptr, mask](u32, T val) { *ptr = val & mask; }; + } + + virtual void VisitComplex(std::function lambda) + { + ret = lambda; + } + }; + + FuncCreatorVisitor v; + Visit(v); + m_WriteFunc = v.ret; +} + +// Define all the public specializations that are exported in MMIOHandlers.h. +MMIO_PUBLIC_SPECIALIZATIONS(); + +} diff --git a/Source/Core/Core/HW/MMIO.h b/Source/Core/Core/HW/MMIO.h new file mode 100644 index 0000000000..41353b7858 --- /dev/null +++ b/Source/Core/Core/HW/MMIO.h @@ -0,0 +1,156 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "Common.h" +#include +#include +#include + +#include "MMIOHandlers.h" + +namespace MMIO +{ + +// There are three main MMIO blocks on the Wii (only one on the GameCube): +// - 0xCC00xxxx: GameCube MMIOs (CP, PE, VI, PI, MI, DSP, DVD, SI, EI, AI, GP) +// - 0xCD00xxxx: Wii MMIOs and GC mirrors (IPC, DVD, SI, EI, AI) +// - 0xCD80xxxx: Mirror of 0xCD00xxxx. +// +// In practice, since the third block is a mirror of the second one, we can +// assume internally that there are only two blocks: one for GC, one for Wii. +enum Block +{ + GC_BLOCK = 0, + WII_BLOCK = 1, + + NUM_BLOCKS +}; +const u32 BLOCK_SIZE = 0x10000; +const u32 NUM_MMIOS = NUM_BLOCKS * BLOCK_SIZE; + +// Compute the internal unique ID for a given MMIO address. This ID is computed +// from a very simple formula: (1 + block_id) * lower_16_bits(address). +// +// The block ID can easily be computed by simply checking bit 24 (CC vs. CD). +inline u32 UniqueID(u32 address) +{ + _dbg_assert_msg_(MEMMAP, ((address & 0xFFFF0000) == 0xCC000000) || + ((address & 0xFFFF0000) == 0xCD000000) || + ((address & 0xFFFF0000) == 0xCD800000), + "Trying to get the ID of a non-existing MMIO address."); + + return (1 + ((address >> 24) & 1)) * (address & 0xFFFF); +} + +// Some utilities functions to define MMIO mappings. +namespace Utils +{ +// Allow grabbing pointers to the high and low part of a 32 bits pointer. +inline u16* LowPart(u32* ptr) { return (u16*)ptr; } +inline u16* LowPart(volatile u32* ptr) { return (u16*)ptr; } +inline u16* HighPart(u32* ptr) { return LowPart(ptr) + 1; } +inline u16* HighPart(volatile u32* ptr) { return LowPart(ptr) + 1; } +} + +class Mapping +{ +public: + // MMIO registration interface. Use this to register new MMIO handlers. + // + // Example usages can be found in just about any HW/ module in Dolphin's + // codebase. +#define REGISTER_FUNCS(Size) \ + void RegisterRead(u32 addr, ReadHandlingMethod* read) \ + { \ + u32 id = UniqueID(addr) / sizeof (u##Size); \ + m_Read##Size##Handlers[id].ResetMethod(read); \ + } \ + void RegisterWrite(u32 addr, WriteHandlingMethod* write) \ + { \ + u32 id = UniqueID(addr) / sizeof (u##Size); \ + m_Write##Size##Handlers[id].ResetMethod(write); \ + } \ + void Register(u32 addr, ReadHandlingMethod* read, \ + WriteHandlingMethod* write) \ + { \ + RegisterRead(addr, read); \ + RegisterWrite(addr, write); \ + } + REGISTER_FUNCS(8) REGISTER_FUNCS(16) REGISTER_FUNCS(32) +#undef REGISTER_FUNCS + + // Direct read/write interface. + // + // These functions allow reading/writing an MMIO register at a given + // address. They are used by the Memory:: access functions, which are + // called in interpreter mode, from Dolphin's own code, or from JIT'd code + // where the access address could not be predicted. + // + // Note that for reads we cannot simply return the read value because C++ + // allows overloading only with parameter types, not return types. +#define READ_FUNC(Size) \ + void Read(u32 addr, u##Size& val) const \ + { \ + u32 id = UniqueID(addr) / sizeof (u##Size); \ + val = m_Read##Size##Handlers[id].Read(addr); \ + } + READ_FUNC(8) READ_FUNC(16) READ_FUNC(32) +#undef READ_FUNC + +#define WRITE_FUNC(Size) \ + void Write(u32 addr, u##Size val) const \ + { \ + u32 id = UniqueID(addr) / sizeof (u##Size); \ + m_Write##Size##Handlers[id].Write(addr, val); \ + } + WRITE_FUNC(8) WRITE_FUNC(16) WRITE_FUNC(32) +#undef WRITE_FUNC + + // Handlers access interface. + // + // Use when you care more about how to access the MMIO register for an + // address than the current value of that register. For example, this is + // what could be used to implement fast MMIO accesses in Dolphin's JIT. + // + // Two variants of each GetHandler function are provided: one that returns + // the handler directly and one that has a pointer parameter to return the + // value. This second variant is needed because C++ doesn't do overloads + // based on return type but only based on argument types. +#define GET_HANDLERS_FUNC(Type, Size) \ + const Type##Handler& GetHandlerFor##Type##Size(u32 addr) const \ + { \ + return m_##Type##Size##Handlers[UniqueID(addr) / sizeof (u##Size)]; \ + } \ + void GetHandlerFor##Type(u32 addr, const Type##Handler** h) const \ + { \ + *h = &GetHandlerFor##Type##Size(addr); \ + } + GET_HANDLERS_FUNC(Read, 8) GET_HANDLERS_FUNC(Read, 16) GET_HANDLERS_FUNC(Read, 32) + GET_HANDLERS_FUNC(Write, 8) GET_HANDLERS_FUNC(Write, 16) GET_HANDLERS_FUNC(Write, 32) +#undef GET_HANDLERS_FUNC + + // Dummy 64 bits variants of these functions. While 64 bits MMIO access is + // not supported, we need these in order to make the code compile. + void Read(u32 addr, u64& val) const { _dbg_assert_(MEMMAP, 0); } + void Write(u32 addr, u64 val) const { _dbg_assert_(MEMMAP, 0); } + +private: + // These arrays contain the handlers for each MMIO access type: read/write + // to 8/16/32 bits. They are indexed using the UniqueID(addr) function + // defined earlier, which maps an MMIO address to a unique ID by using the + // MMIO block ID. + // + // Each array contains NUM_MMIOS / sizeof (AccessType) because larger + // access types mean less possible adresses (assuming aligned only + // accesses). +#define HANDLERS(Size) \ + std::array, NUM_MMIOS / sizeof (u##Size)> m_Read##Size##Handlers; \ + std::array, NUM_MMIOS / sizeof (u##Size)> m_Write##Size##Handlers; + HANDLERS(8) HANDLERS(16) HANDLERS(32) +#undef HANDLERS +}; + +} diff --git a/Source/Core/Core/HW/MMIOHandlers.h b/Source/Core/Core/HW/MMIOHandlers.h new file mode 100644 index 0000000000..732704efae --- /dev/null +++ b/Source/Core/Core/HW/MMIOHandlers.h @@ -0,0 +1,201 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "Common.h" + +#include +#include + +// All the templated and very repetitive MMIO-related code is isolated in this +// file for easier reading. It mostly contains code related to handling methods +// (including the declaration of the public functions for creating handling +// method objects), visitors for these handling methods, and interface of the +// handler classes. +// +// This code is very genericized (aka. lots of templates) in order to handle +// u8/u16/u32 with the same code while providing type safety: it is impossible +// to mix code from these types, and the type system enforces it. + +namespace MMIO +{ + +class Mapping; + +// Read and write handling methods are separated for type safety. On top of +// that, some handling methods require different arguments for reads and writes +// (Complex, for example). +template class ReadHandlingMethod; +template class WriteHandlingMethod; + +// Constant: use when the value read on this MMIO is always the same. This is +// only for reads. +template ReadHandlingMethod* Constant(T value); + +// Nop: use for writes that shouldn't have any effect and shouldn't log an +// error either. +template WriteHandlingMethod* Nop(); + +// Direct: use when all the MMIO does is read/write the given value to/from a +// global variable, with an optional mask applied on the read/written value. +template ReadHandlingMethod* DirectRead(const T* addr, u32 mask = 0xFFFFFFFF); +template ReadHandlingMethod* DirectRead(volatile const T* addr, u32 mask = 0xFFFFFFFF); +template WriteHandlingMethod* DirectWrite(T* addr, u32 mask = 0xFFFFFFFF); +template WriteHandlingMethod* DirectWrite(volatile T* addr, u32 mask = 0xFFFFFFFF); + +// Complex: use when no other handling method fits your needs. These allow you +// to directly provide a function that will be called when a read/write needs +// to be done. +template ReadHandlingMethod* ComplexRead(std::function); +template WriteHandlingMethod* ComplexWrite(std::function); + +// Invalid: log an error and return -1 in case of a read. These are the default +// handlers set for all MMIO types. +template ReadHandlingMethod* InvalidRead(); +template WriteHandlingMethod* InvalidWrite(); + +// {Read,Write}To{Smaller,Larger}: these functions are not themselves handling +// methods but will try to combine accesses to two handlers into one new +// handler object. +// +// This is used for example when 32 bit reads have the exact same handling as +// 16 bit. Handlers need to be registered for both 32 and 16, and it would be +// repetitive and unoptimal to require users to write the same handling code in +// both cases. Instead, an MMIO module can simply define all handlers in terms +// of 16 bit reads, then use ReadToSmaller to convert u32 reads to u16 +// reads. +// +// Internally, these size conversion functions have some magic to make the +// combined handlers as fast as possible. For example, if the two underlying +// u16 handlers for a u32 reads are Direct to consecutive memory addresses, +// they can be transformed into a Direct u32 access. +// +// Warning: unlike the other handling methods, *ToSmaller are obviously not +// available for u8, and *ToLarger are not available for u32. +template ReadHandlingMethod* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); +template WriteHandlingMethod* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); +template ReadHandlingMethod* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift); + +// Use these visitors interfaces if you need to write code that performs +// different actions based on the handling method used by a handler. Write your +// visitor implementing that interface, then use handler->VisitHandlingMethod +// to run the proper function. +template +class ReadHandlingMethodVisitor +{ +public: + virtual void VisitConstant(T value) = 0; + virtual void VisitDirect(const T* addr, u32 mask) = 0; + virtual void VisitComplex(std::function lambda) = 0; +}; +template +class WriteHandlingMethodVisitor +{ +public: + virtual void VisitNop() = 0; + virtual void VisitDirect(T* addr, u32 mask) = 0; + virtual void VisitComplex(std::function lambda) = 0; +}; + +// These classes are INTERNAL. Do not use outside of the MMIO implementation +// code. Unfortunately, because we want to make Read() and Write() fast and +// inlinable, we need to provide some of the implementation of these two +// classes here and can't just use a forward declaration. +template +class ReadHandler : public NonCopyable +{ +public: + ReadHandler(); + + // Takes ownership of "method". + ReadHandler(ReadHandlingMethod* method); + + ~ReadHandler(); + + // Entry point for read handling method visitors. + void Visit(ReadHandlingMethodVisitor& visitor) const; + + T Read(u32 addr) const + { + return m_ReadFunc(addr); + } + + // Internal method called when changing the internal method object. Its + // main role is to make sure the read function is updated at the same time. + void ResetMethod(ReadHandlingMethod* method); + +private: + std::unique_ptr> m_Method; + std::function m_ReadFunc; +}; +template +class WriteHandler : public NonCopyable +{ +public: + WriteHandler(); + + // Takes ownership of "method". + WriteHandler(WriteHandlingMethod* method); + + ~WriteHandler(); + + // Entry point for write handling method visitors. + void Visit(WriteHandlingMethodVisitor& visitor) const; + + void Write(u32 addr, T val) const + { + m_WriteFunc(addr, val); + } + + // Internal method called when changing the internal method object. Its + // main role is to make sure the write function is updated at the same + // time. + void ResetMethod(WriteHandlingMethod* method); + +private: + std::unique_ptr> m_Method; + std::function m_WriteFunc; +}; + +// Boilerplate boilerplate boilerplate. +// +// This is used to be able to avoid putting the templates implementation in the +// header files and slow down compilation times. Instead, we declare 3 +// specializations in the header file as already implemented in another +// compilation unit: u8, u16, u32. +// +// The "MaybeExtern" is there because that same macro is used for declaration +// (where MaybeExtern = "extern") and definition (MaybeExtern = ""). +#define MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, T) \ + MaybeExtern template ReadHandlingMethod* Constant(T value); \ + MaybeExtern template WriteHandlingMethod* Nop(); \ + MaybeExtern template ReadHandlingMethod* DirectRead(const T* addr, u32 mask); \ + MaybeExtern template ReadHandlingMethod* DirectRead(volatile const T* addr, u32 mask); \ + MaybeExtern template WriteHandlingMethod* DirectWrite(T* addr, u32 mask); \ + MaybeExtern template WriteHandlingMethod* DirectWrite(volatile T* addr, u32 mask); \ + MaybeExtern template ReadHandlingMethod* ComplexRead(std::function); \ + MaybeExtern template WriteHandlingMethod* ComplexWrite(std::function); \ + MaybeExtern template ReadHandlingMethod* InvalidRead(); \ + MaybeExtern template WriteHandlingMethod* InvalidWrite(); \ + MaybeExtern template class ReadHandler; \ + MaybeExtern template class WriteHandler + +#define MMIO_SPECIAL_PUBLIC_SPECIALIZATIONS(MaybeExtern) \ + MaybeExtern template ReadHandlingMethod* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \ + MaybeExtern template ReadHandlingMethod* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \ + MaybeExtern template WriteHandlingMethod* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \ + MaybeExtern template WriteHandlingMethod* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \ + MaybeExtern template ReadHandlingMethod* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift); \ + MaybeExtern template ReadHandlingMethod* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift) + +#define MMIO_PUBLIC_SPECIALIZATIONS(MaybeExtern) \ + MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u8); \ + MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u16); \ + MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u32); \ + MMIO_SPECIAL_PUBLIC_SPECIALIZATIONS(MaybeExtern); + +MMIO_PUBLIC_SPECIALIZATIONS(extern) + +} diff --git a/Source/Core/Core/HW/Memmap.cpp b/Source/Core/Core/HW/Memmap.cpp index 82f3141040..d10cf90f0a 100644 --- a/Source/Core/Core/HW/Memmap.cpp +++ b/Source/Core/Core/HW/Memmap.cpp @@ -34,6 +34,7 @@ #include "../ConfigManager.h" #include "../Debugger/Debugger_SymbolMap.h" #include "VideoBackendBase.h" +#include "MMIO.h" namespace Memory { @@ -83,6 +84,9 @@ u8 *m_pVirtualUncachedEXRAM; // wii only u8 *m_pVirtualL1Cache; u8 *m_pVirtualFakeVMEM; +// MMIO mapping object. +MMIO::Mapping* mmio_mapping; + // ================================= // Read and write shortcuts // ---------------- @@ -348,6 +352,8 @@ void Init() if (bFakeVMEM) flags |= MV_FAKE_VMEM; base = MemoryMap_Setup(views, num_views, flags, &g_arena); + mmio_mapping = new MMIO::Mapping(); + if (wii) InitHWMemFuncsWii(); else @@ -382,6 +388,7 @@ void Shutdown() MemoryMap_Shutdown(views, num_views, flags, &g_arena); g_arena.ReleaseSpace(); base = NULL; + delete mmio_mapping; INFO_LOG(MEMMAP, "Memory system shut down."); } diff --git a/Source/Core/Core/HW/Memmap.h b/Source/Core/Core/HW/Memmap.h index 1f89148c56..d2ea967496 100644 --- a/Source/Core/Core/HW/Memmap.h +++ b/Source/Core/Core/HW/Memmap.h @@ -28,6 +28,7 @@ // Global declarations class PointerWrap; +namespace MMIO { class Mapping; } typedef void (*writeFn8 )(const u8, const u32); typedef void (*writeFn16)(const u16,const u32); @@ -83,6 +84,9 @@ enum #endif }; +// MMIO mapping object. +extern MMIO::Mapping* mmio_mapping; + // Init and Shutdown bool IsInitialized(); void Init(); diff --git a/Source/Core/Core/HW/MemmapFunctions.cpp b/Source/Core/Core/HW/MemmapFunctions.cpp index 40bc5f4a3a..eb335228a5 100644 --- a/Source/Core/Core/HW/MemmapFunctions.cpp +++ b/Source/Core/Core/HW/MemmapFunctions.cpp @@ -24,6 +24,7 @@ #include "../Core.h" #include "../PowerPC/PowerPC.h" #include "VideoBackendBase.h" +#include "MMIO.h" #ifdef USE_GDBSTUB #include "../PowerPC/GDBStub.h"