// Copyright 2014 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #include #include "Common/Assert.h" #include "Common/CommonTypes.h" #include "Core/HW/MMIO.h" #include "Core/HW/MMIOHandlers.h" 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() {} void AcceptReadVisitor(ReadHandlingMethodVisitor& v) const override { 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() {} void AcceptWriteVisitor(WriteHandlingMethodVisitor& v) const override { 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() {} void AcceptReadVisitor(ReadHandlingMethodVisitor& v) const override { v.VisitDirect(addr_, mask_); } void AcceptWriteVisitor(WriteHandlingMethodVisitor& v) const override { 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() {} void AcceptReadVisitor(ReadHandlingMethodVisitor& v) const override { v.VisitComplex(&read_lambda_); } void AcceptWriteVisitor(WriteHandlingMethodVisitor& v) const override { 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 %zu bits from an invalid MMIO (addr=%08x)", 8 * sizeof(T), addr); return -1; }); } template WriteHandlingMethod* InvalidWrite() { return ComplexWrite([](u32 addr, T val) { ERROR_LOG(MEMMAP, "Trying to write %zu bits to an invalid MMIO (addr=%08x, val=%08x)", 8 * sizeof(T), 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; ReadHandler* high_part = &mmio->GetHandlerForRead(high_part_addr); ReadHandler* low_part = &mmio->GetHandlerForRead(low_part_addr); // TODO(delroth): optimize return ComplexRead([=](u32 addr) { return ((T)high_part->Read(high_part_addr) << (8 * sizeof(ST))) | low_part->Read(low_part_addr); }); } template WriteHandlingMethod* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr) { typedef typename SmallerAccessSize::value ST; WriteHandler* high_part = &mmio->GetHandlerForWrite(high_part_addr); WriteHandler* low_part = &mmio->GetHandlerForWrite(low_part_addr); // TODO(delroth): optimize return ComplexWrite([=](u32 addr, T val) { high_part->Write(high_part_addr, val >> (8 * sizeof(ST))); low_part->Write(low_part_addr, (ST)val); }); } template ReadHandlingMethod* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift) { typedef typename LargerAccessSize::value LT; ReadHandler* large = &mmio->GetHandlerForRead(larger_addr); // 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() { } template ReadHandler::ReadHandler(ReadHandlingMethod* method) : m_Method(nullptr) { ResetMethod(method); } template ReadHandler::~ReadHandler() { } template void ReadHandler::Visit(ReadHandlingMethodVisitor& visitor) { if (!m_Method) InitializeInvalid(); m_Method->AcceptReadVisitor(visitor); } template void ReadHandler::ResetMethod(ReadHandlingMethod* method) { m_Method.reset(method); struct FuncCreatorVisitor : public ReadHandlingMethodVisitor { std::function ret; void VisitConstant(T value) override { ret = [value](u32) { return value; }; } void VisitDirect(const T* addr, u32 mask) override { ret = [addr, mask](u32) { return *addr & mask; }; } void VisitComplex(const std::function* lambda) override { ret = *lambda; } }; FuncCreatorVisitor v; Visit(v); m_ReadFunc = v.ret; } template WriteHandler::WriteHandler() { } template WriteHandler::WriteHandler(WriteHandlingMethod* method) : m_Method(nullptr) { ResetMethod(method); } template WriteHandler::~WriteHandler() { } template void WriteHandler::Visit(WriteHandlingMethodVisitor& visitor) { if (!m_Method) InitializeInvalid(); m_Method->AcceptWriteVisitor(visitor); } template void WriteHandler::ResetMethod(WriteHandlingMethod* method) { m_Method.reset(method); struct FuncCreatorVisitor : public WriteHandlingMethodVisitor { std::function ret; void VisitNop() override { ret = [](u32, T) {}; } void VisitDirect(T* ptr, u32 mask) override { ret = [ptr, mask](u32, T val) { *ptr = val & mask; }; } void VisitComplex(const std::function* lambda) override { ret = *lambda; } }; FuncCreatorVisitor v; Visit(v); m_WriteFunc = v.ret; } // Define all the public specializations that are exported in MMIOHandlers.h. #define MaybeExtern MMIO_PUBLIC_SPECIALIZATIONS() #undef MaybeExtern }