Implementing a rectangle memory accesor for the Cartridge (for the LoRom)

This commit is contained in:
AnonymusRaccoon
2020-01-29 18:03:29 +01:00
parent 589252b34e
commit 6429e9a284
17 changed files with 234 additions and 74 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
#include "IMemory.hpp"
#include <algorithm>
namespace ComSquare
namespace ComSquare::Memory
{
void IMemory::setMemoryRegion(uint24_t start, uint24_t end)
{
+3 -3
View File
@@ -10,7 +10,7 @@
#include <vector>
#include "../Models/Ints.hpp"
namespace ComSquare
namespace ComSquare::Memory
{
//! @brief Common interface implemented by all components mapping memory.
class IMemory {
@@ -38,10 +38,10 @@ namespace ComSquare
//! @brief Return true if this component has mapped the address.
//! @param addr The address to check.
//! @return True if this address is mapped to the component. False otherwise.
bool hasMemoryAt(uint24_t addr);
virtual bool hasMemoryAt(uint24_t addr);
//! @brief Get the first address mapped to this component.
//! @return the _start value.
uint24_t getStart();
virtual uint24_t getStart();
};
};
+65
View File
@@ -0,0 +1,65 @@
//
// Created by anonymus-raccoon on 1/29/20.
//
#include "IRectangleMemory.hpp"
#include "../Exceptions/InvalidAddress.hpp"
namespace ComSquare::Memory
{
uint8_t IRectangleMemory::read(uint24_t addr)
{
uint8_t bank = addr >> 16u;
uint16_t page = addr;
unsigned bankCount = bank - this->_startBank;
unsigned pageCount = this->_endPage - this->_startPage;
if (bank < this->_startBank || bank >= this->_endBank)
throw InvalidAddress("Rectangle memory read Invalid Bank", addr);
if (page < this->_startPage || page >= this->_endPage)
throw InvalidAddress("Rectangle memory read Invalid Page", addr);
page -= this->_startPage;
page += pageCount * bankCount;
return this->read_internal(page);
}
void IRectangleMemory::write(uint24_t addr, uint8_t data)
{
uint8_t bank = addr >> 16u;
uint16_t page = addr;
unsigned bankCount = bank - this->_startBank;
unsigned pageCount = this->_endPage - this->_startPage;
if (bank < this->_startBank || bank >= this->_endBank)
throw InvalidAddress("Rectangle memory write Invalid Bank", addr);
if (page < this->_startPage || page >= this->_endPage)
throw InvalidAddress("Rectangle memory write Invalid Page", addr);
page -= this->_startPage;
page += pageCount * bankCount;
this->write_internal(page, data);
}
bool IRectangleMemory::hasMemoryAt(uint24_t addr)
{
uint8_t bank = addr >> 16u;
uint16_t page = addr;
if (this->_startBank <= bank && bank < this->_endBank)
if (this->_startPage <= page && page < this->_endPage)
return true;
return false;
}
uint24_t IRectangleMemory::getStart()
{
return this->_startBank + this->_startPage;
}
void IRectangleMemory::setMemoryRegion(uint8_t startBank, uint8_t endBank, uint16_t startPage, uint16_t endPage)
{
this->_startBank = startBank;
this->_endBank = endBank;
this->_startPage = startPage;
this->_endPage = endPage;
}
}
+62
View File
@@ -0,0 +1,62 @@
//
// Created by anonymus-raccoon on 1/29/20.
//
#ifndef COMSQUARE_IRECTANGLEMEMORY_HPP
#define COMSQUARE_IRECTANGLEMEMORY_HPP
#include "IMemory.hpp"
namespace ComSquare::Memory
{
//! @brief Superset of the IMemory to map non continuous rectangle to the memory. (A rectangle that spam across more than one bank but that does not start at 0000 or end at FFFF).
class IRectangleMemory : public IMemory {
private:
//! @brief The first bank to map to.
uint8_t _startBank = 0;
//! @brief The last bank to map to.
uint8_t _endBank = 0;
//! @brief The first address of each bank to map.
uint16_t _startPage = 0;
//! @brief The last address of each bank to map.
uint16_t _endPage = 0;
public:
//! @brief Read data from the component using the same method as the basic IMemory.
//! @param addr The local address to read from. 0x0 should refer to the first byte of this component on the fist bank. This method is responsible of mapping to the component's read.
//! @throw InvalidAddress if the address is not mapped to the component.
//! @return Return the data at the address given as parameter.
uint8_t read(uint24_t addr) override;
//! @brief Write data to this component using the same method as the basic IMemory.
//! @param addr The local address to write data 0x0 should refer to the first byte of this component on the fist bank. This method is responsible of mapping to the component's write.
//! @param data The new data to write.
//! @throw InvalidAddress if the address is not mapped to the component.
void write(uint24_t addr, uint8_t data) override;
//! @brief Internal component read. Implement this as you would implement a basic IMemory's read.
//! @param addr The local address to read from. 0x0 refer to the first byte of your data and the address is in the component's space. That means that you can consider this address as continuous
//! @throw This function should thrown an InvalidAddress for address that are not mapped to the component.
//! @return Return the data at the address given as parameter.
virtual uint8_t read_internal(uint24_t addr) = 0;
//! @brief Internal component write. Implement this as you would implement a basic IMemory's write.
//! @param addr The local address to write to. 0x0 refer to the first byte of your data and the address is in the component's space. That means that you can consider this address as continuous
//! @param data The new data to write.
//! @throw This function should thrown an InvalidAddress for address that are not mapped to the component.
virtual void write_internal(uint24_t addr, uint8_t data) = 0;
//! @brief Return true if this component has mapped the address.
//! @param addr The address to check.
//! @return True if this address is mapped to the component. False otherwise.
bool hasMemoryAt(uint24_t addr) override;
//! @brief Get the first address mapped to this component.
//! @return the _start value.
uint24_t getStart() override;
//! @brief Change starting and ending points of this mapped memory.
//! @param startBank The first bank mapped to this component.
//! @param endBank The last bank mapped to this component.
//! @param startPage The first page mapped to this component (every mapped banks will have this page mapped)
//! @param endPage The end page mapped to this component (every mapped banks will have this pages lower than this mapped)
//! @warning The start/end address should be a rectangle. To mirror memory, use the MemoryShadow class and not this one.
void setMemoryRegion(uint8_t startBank, uint8_t endBank, uint16_t startPage, uint16_t endPage);
};
}
#endif //COMSQUARE_IRECTANGLEMEMORY_HPP
+2 -2
View File
@@ -8,7 +8,7 @@
#include "../SNES.hpp"
#include "MemoryShadow.hpp"
namespace ComSquare
namespace ComSquare::Memory
{
std::shared_ptr<IMemory> MemoryBus::getAccessor(uint24_t addr)
{
@@ -78,6 +78,6 @@ namespace ComSquare
for (uint24_t i = 0x800000; i < 0xC00000; i += 0x10000)
this->_mirrorComponents(console, i);
// TODO should map sram, cartridge etc via the mapping mode of the cartridge.
// TODO should map SRam, cartridge etc via the mapping mode of the cartridge.
}
}
+39 -28
View File
@@ -12,34 +12,45 @@
namespace ComSquare
{
//! @brief The memory bus is the component responsible of mapping addresses to components address and transmitting the data.
class MemoryBus {
private:
//! @brief The list of components registered inside the bus. Every components that can read/write to a public address should be in this vector.
std::vector<std::shared_ptr<IMemory>> _memoryAccessors;
//! @brief Helper function to get the components that is responsible of read/write at an address.
//! @param addr The address you want to look for.
//! @return The components responsible for the address param or nullptr if none was found.
std::shared_ptr<IMemory> getAccessor(uint24_t addr);
//! @brief The last value read via the memory bus.
uint8_t _openbus = 0;
//! @brief WRam, CPU, PPU & ALU registers are mirrored to all banks of Q1 & Q3. This function is used for the mirroring.
//! @param console All the components.
//! @param i Base address for the mirrors.
inline void _mirrorComponents(struct SNES &console, int i);
public:
//! @brief Read data at a global address.
//! @param addr The address to read from.
//! @return The value that the component returned for this address. If the address was mapped to ram, it simply returned the value. If the address was mapped to a register the component returned the register.
uint8_t read(uint24_t addr);
//! @brief Write a data to a global address.
//! @param addr The address to write to.
//! @param data The data to write.
void write(uint24_t addr, uint8_t data);
//! @brief Map components to the address space using the currently loaded cartridge to set the right mapping mode.
//! @param console All the components.
void mapComponents(struct SNES &console);
};
struct SNES;
namespace Memory
{
//! @brief The memory bus is the component responsible of mapping addresses to components address and transmitting the data.
class MemoryBus {
private:
//! @brief The list of components registered inside the bus. Every components that can read/write to a public address should be in this vector.
std::vector<std::shared_ptr<IMemory>> _memoryAccessors;
//! @brief Helper function to get the components that is responsible of read/write at an address.
//! @param addr The address you want to look for.
//! @return The components responsible for the address param or nullptr if none was found.
std::shared_ptr<IMemory> getAccessor(uint24_t addr);
//! @brief The last value read via the memory bus.
uint8_t _openbus = 0;
//! @brief WRam, CPU, PPU & ALU registers are mirrored to all banks of Q1 & Q3. This function is used for the mirroring.
//! @param console All the components.
//! @param i Base address for the mirrors.
inline void _mirrorComponents(SNES &console, int i);
public:
//! @brief Read data at a global address.
//! @param addr The address to read from.
//! @return The value that the component returned for this address. If the address was mapped to ram, it simply returned the value. If the address was mapped to a register the component returned the register.
uint8_t read(uint24_t addr);
//! @brief Write a data to a global address.
//! @param addr The address to write to.
//! @param data The data to write.
void write(uint24_t addr, uint8_t data);
//! @brief Map components to the address space using the currently loaded cartridge to set the right mapping mode.
//! @param console All the components.
void mapComponents(SNES &console);
};
}
}