mirror of
https://github.com/zoriya/ComSquare.git
synced 2026-06-02 18:21:19 +00:00
Adding filters
This commit is contained in:
+2
-2
@@ -79,7 +79,7 @@ add_executable(unit_tests
|
|||||||
sources/CPU/Instructions/TransferRegisters.cpp
|
sources/CPU/Instructions/TransferRegisters.cpp
|
||||||
tests/CPU/TransferRegisters.cpp
|
tests/CPU/TransferRegisters.cpp
|
||||||
sources/CPU/AddressingModes.cpp
|
sources/CPU/AddressingModes.cpp
|
||||||
)
|
sources/Models/Components.hpp)
|
||||||
|
|
||||||
# include criterion & coverage
|
# include criterion & coverage
|
||||||
target_link_libraries(unit_tests criterion -lgcov)
|
target_link_libraries(unit_tests criterion -lgcov)
|
||||||
@@ -174,7 +174,7 @@ add_executable(ComSquare
|
|||||||
sources/Debugger/MemoryBusDebug.cpp
|
sources/Debugger/MemoryBusDebug.cpp
|
||||||
sources/Debugger/MemoryBusDebug.hpp
|
sources/Debugger/MemoryBusDebug.hpp
|
||||||
sources/Debugger/ClosableWindow.hpp
|
sources/Debugger/ClosableWindow.hpp
|
||||||
)
|
sources/Models/Components.hpp)
|
||||||
|
|
||||||
target_compile_definitions(ComSquare PUBLIC DEBUGGER_ENABLED)
|
target_compile_definitions(ComSquare PUBLIC DEBUGGER_ENABLED)
|
||||||
|
|
||||||
|
|||||||
+14
-4
@@ -23,6 +23,16 @@ namespace ComSquare::APU
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string APU::getName()
|
||||||
|
{
|
||||||
|
return "APU";
|
||||||
|
}
|
||||||
|
|
||||||
|
Component APU::getComponent()
|
||||||
|
{
|
||||||
|
return Apu;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t APU::_internalRead(uint24_t addr) {
|
uint8_t APU::_internalRead(uint24_t addr) {
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case 0x0000 ... 0x00EF:
|
case 0x0000 ... 0x00EF:
|
||||||
@@ -359,10 +369,10 @@ namespace ComSquare::APU
|
|||||||
}
|
}
|
||||||
|
|
||||||
MemoryMap::MemoryMap() :
|
MemoryMap::MemoryMap() :
|
||||||
Page0(0x00F0, "APU's Page 0"),
|
Page0(0x00F0, Apu, "APU's Page 0"),
|
||||||
Page1(0x0100, "APU's Page 1"),
|
Page1(0x0100, Apu, "APU's Page 1"),
|
||||||
Memory(0xFDC0, "APU's Ram"),
|
Memory(0xFDC0, Apu, "APU's Ram"),
|
||||||
IPL(0x0040, "IPL Rom")
|
IPL(0x0040, Apu, "IPL Rom")
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,6 +158,9 @@ namespace ComSquare::APU
|
|||||||
//! @brief Get the name of this accessor (used for debug purpose)
|
//! @brief Get the name of this accessor (used for debug purpose)
|
||||||
std::string getName() override;
|
std::string getName() override;
|
||||||
|
|
||||||
|
//! @brief Get the component of this accessor (used for debug purpose)
|
||||||
|
Component getComponent() override;
|
||||||
|
|
||||||
//! @brief Current state of APU CPU
|
//! @brief Current state of APU CPU
|
||||||
StateMode _state = Running;
|
StateMode _state = Running;
|
||||||
|
|
||||||
|
|||||||
@@ -593,4 +593,9 @@ namespace ComSquare::APU::DSP
|
|||||||
{
|
{
|
||||||
return "DSP";
|
return "DSP";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component DSP::getComponent()
|
||||||
|
{
|
||||||
|
return Apu;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -143,6 +143,9 @@ namespace ComSquare::APU::DSP
|
|||||||
|
|
||||||
//! @brief Get the name of this accessor (used for debug purpose)
|
//! @brief Get the name of this accessor (used for debug purpose)
|
||||||
std::string getName() override;
|
std::string getName() override;
|
||||||
|
|
||||||
|
//! @brief Get the component of this accessor (used for debug purpose)
|
||||||
|
Component getComponent() override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,9 +52,4 @@ namespace ComSquare::APU
|
|||||||
this->RET();
|
this->RET();
|
||||||
return 6;
|
return 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string APU::getName()
|
|
||||||
{
|
|
||||||
return "APU";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -413,4 +413,14 @@ namespace ComSquare::CPU
|
|||||||
{
|
{
|
||||||
return this->_bus->read(++this->_registers.s) + (this->_bus->read(++this->_registers.s) << 8u);
|
return this->_bus->read(++this->_registers.s) + (this->_bus->read(++this->_registers.s) << 8u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string CPU::getName()
|
||||||
|
{
|
||||||
|
return "CPU";
|
||||||
|
}
|
||||||
|
|
||||||
|
Component CPU::getComponent()
|
||||||
|
{
|
||||||
|
return Cpu;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "../Memory/MemoryBus.hpp"
|
#include "../Memory/MemoryBus.hpp"
|
||||||
#include "../Models/Int24.hpp"
|
#include "../Models/Int24.hpp"
|
||||||
#include "../Cartridge/Cartridge.hpp"
|
#include "../Cartridge/Cartridge.hpp"
|
||||||
|
#include "../Memory/AMemory.hpp"
|
||||||
|
|
||||||
namespace ComSquare::CPU
|
namespace ComSquare::CPU
|
||||||
{
|
{
|
||||||
@@ -573,6 +574,9 @@ namespace ComSquare::CPU
|
|||||||
//! @brief Get the name of this accessor (used for debug purpose)
|
//! @brief Get the name of this accessor (used for debug purpose)
|
||||||
std::string getName() override;
|
std::string getName() override;
|
||||||
|
|
||||||
|
//! @brief Get the component of this accessor (used for debug purpose)
|
||||||
|
Component getComponent() override;
|
||||||
|
|
||||||
//! @brief Reset interrupt - Called on boot and when the reset button is pressed.
|
//! @brief Reset interrupt - Called on boot and when the reset button is pressed.
|
||||||
virtual void RESB();
|
virtual void RESB();
|
||||||
|
|
||||||
|
|||||||
@@ -307,9 +307,4 @@ namespace ComSquare::CPU
|
|||||||
{
|
{
|
||||||
this->_registers.pac = value;
|
this->_registers.pac = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CPU::getName()
|
|
||||||
{
|
|
||||||
return "CPU";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
namespace ComSquare::Cartridge
|
namespace ComSquare::Cartridge
|
||||||
{
|
{
|
||||||
Cartridge::Cartridge(const std::string &romPath)
|
Cartridge::Cartridge(const std::string &romPath)
|
||||||
: Ram::Ram(0, "Cartridge")
|
: Ram::Ram(0, Rom, "Cartridge")
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (romPath.empty())
|
if (romPath.empty())
|
||||||
|
|||||||
@@ -30,6 +30,60 @@ namespace ComSquare::Debugger
|
|||||||
this->_ui.log->horizontalHeader()->setSectionsMovable(true);
|
this->_ui.log->horizontalHeader()->setSectionsMovable(true);
|
||||||
for (int i = 0; i < this->_model.column; i++)
|
for (int i = 0; i < this->_model.column; i++)
|
||||||
this->_ui.log->setColumnWidth(i, this->_ui.log->width());
|
this->_ui.log->setColumnWidth(i, this->_ui.log->width());
|
||||||
|
|
||||||
|
QMainWindow::connect(this->_ui.fromAPU, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[0].apu = checked;
|
||||||
|
});
|
||||||
|
QMainWindow::connect(this->_ui.fromCPU, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[0].cpu = checked;
|
||||||
|
});
|
||||||
|
QMainWindow::connect(this->_ui.fromOAM, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[0].oamram = checked;
|
||||||
|
});
|
||||||
|
QMainWindow::connect(this->_ui.fromPPU, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[0].ppu = checked;
|
||||||
|
});
|
||||||
|
QMainWindow::connect(this->_ui.fromROM, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[0].rom = checked;
|
||||||
|
});
|
||||||
|
QMainWindow::connect(this->_ui.fromSRAM, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[0].sram = checked;
|
||||||
|
});
|
||||||
|
QMainWindow::connect(this->_ui.fromVRAM, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[0].vram = checked;
|
||||||
|
});
|
||||||
|
QMainWindow::connect(this->_ui.fromWRAM, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[0].wram = checked;
|
||||||
|
});
|
||||||
|
QMainWindow::connect(this->_ui.fromCG, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[0].cgram = checked;
|
||||||
|
});
|
||||||
|
|
||||||
|
QMainWindow::connect(this->_ui.toAPU, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[1].apu = checked;
|
||||||
|
});
|
||||||
|
QMainWindow::connect(this->_ui.toCPU, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[1].cpu = checked;
|
||||||
|
});
|
||||||
|
QMainWindow::connect(this->_ui.toOAM, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[1].oamram = checked;
|
||||||
|
});
|
||||||
|
QMainWindow::connect(this->_ui.toPPU, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[1].ppu = checked;
|
||||||
|
});
|
||||||
|
QMainWindow::connect(this->_ui.toSRAM, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[1].sram = checked;
|
||||||
|
});
|
||||||
|
QMainWindow::connect(this->_ui.toVRAM, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[1].vram = checked;
|
||||||
|
});
|
||||||
|
QMainWindow::connect(this->_ui.toWRAM, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[1].wram = checked;
|
||||||
|
});
|
||||||
|
QMainWindow::connect(this->_ui.toCG, &QCheckBox::toggled, [this](bool checked) {
|
||||||
|
this->_proxy.filters[1].cgram = checked;
|
||||||
|
});
|
||||||
|
|
||||||
this->_window->show();
|
this->_window->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +207,27 @@ bool BusLoggerProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourcePa
|
|||||||
{
|
{
|
||||||
ComSquare::Debugger::BusLog log = this->_parent.getLogAt(sourceRow);
|
ComSquare::Debugger::BusLog log = this->_parent.getLogAt(sourceRow);
|
||||||
|
|
||||||
// if (log.accessor && log.accessor->getName() == "Cartridge")
|
if (!log.accessor)
|
||||||
// return false;
|
return true;
|
||||||
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
|
ComSquare::Component component = log.accessor->getComponent();
|
||||||
|
switch (component) {
|
||||||
|
case ComSquare::Component::Cpu:
|
||||||
|
return this->filters[log.write].cpu;
|
||||||
|
case ComSquare::Component::Ppu:
|
||||||
|
return this->filters[log.write].ppu;
|
||||||
|
case ComSquare::Component::Apu:
|
||||||
|
return this->filters[log.write].apu;
|
||||||
|
case ComSquare::Component::Rom:
|
||||||
|
return this->filters[log.write].rom;
|
||||||
|
case ComSquare::Component::WRam:
|
||||||
|
return this->filters[log.write].wram;
|
||||||
|
case ComSquare::Component::VRam:
|
||||||
|
return this->filters[log.write].vram;
|
||||||
|
case ComSquare::Component::CGRam:
|
||||||
|
return this->filters[log.write].cgram;
|
||||||
|
case ComSquare::Component::OAMRam:
|
||||||
|
return this->filters[log.write].oamram;
|
||||||
|
case ComSquare::Component::SRam:
|
||||||
|
return this->filters[log.write].sram;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,10 +81,8 @@ protected:
|
|||||||
//! @brief Function that filter logs.
|
//! @brief Function that filter logs.
|
||||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||||
public:
|
public:
|
||||||
//! @brief Currently enabled read filters
|
//! @brief Currently enabled filters, index 0 is for reads, index 1 for writes.
|
||||||
ComSquare::Debugger::BusLoggerFilters readFilters = ComSquare::Debugger::BusLoggerFilters();
|
ComSquare::Debugger::BusLoggerFilters filters[2] = {ComSquare::Debugger::BusLoggerFilters(), ComSquare::Debugger::BusLoggerFilters()};
|
||||||
//! @brief Currently enabled write filters
|
|
||||||
ComSquare::Debugger::BusLoggerFilters writeFilters = ComSquare::Debugger::BusLoggerFilters();
|
|
||||||
|
|
||||||
BusLoggerProxy(BusLogModel &parent);
|
BusLoggerProxy(BusLogModel &parent);
|
||||||
BusLoggerProxy(const BusLoggerProxy &) = delete;
|
BusLoggerProxy(const BusLoggerProxy &) = delete;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "../Models/Int24.hpp"
|
#include "../Models/Int24.hpp"
|
||||||
|
#include "../Models/Components.hpp"
|
||||||
|
|
||||||
namespace ComSquare::Memory
|
namespace ComSquare::Memory
|
||||||
{
|
{
|
||||||
@@ -48,6 +49,8 @@ namespace ComSquare::Memory
|
|||||||
virtual bool isMirror();
|
virtual bool isMirror();
|
||||||
//! @brief Get the name of this accessor (used for debug purpose)
|
//! @brief Get the name of this accessor (used for debug purpose)
|
||||||
virtual std::string getName() = 0;
|
virtual std::string getName() = 0;
|
||||||
|
//! @brief Get the component of this accessor (used for debug purpose)
|
||||||
|
virtual Component getComponent() = 0;
|
||||||
//! @brief Get the name of the data at the address
|
//! @brief Get the name of the data at the address
|
||||||
//! @param addr The address (in local space)
|
//! @param addr The address (in local space)
|
||||||
virtual std::string getValueName(uint24_t addr);
|
virtual std::string getValueName(uint24_t addr);
|
||||||
|
|||||||
@@ -38,4 +38,9 @@ namespace ComSquare::Memory
|
|||||||
{
|
{
|
||||||
return this->_initial->getName();
|
return this->_initial->getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component MemoryShadow::getComponent()
|
||||||
|
{
|
||||||
|
return this->_initial->getComponent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -36,6 +36,8 @@ namespace ComSquare::Memory
|
|||||||
bool isMirror() override;
|
bool isMirror() override;
|
||||||
//! @brief Get the name of this accessor (used for debug purpose)
|
//! @brief Get the name of this accessor (used for debug purpose)
|
||||||
std::string getName() override;
|
std::string getName() override;
|
||||||
|
//! @brief Get the component of this accessor (used for debug purpose)
|
||||||
|
Component getComponent() override;
|
||||||
//! @brief Return the memory accessor this accessor mirror if any
|
//! @brief Return the memory accessor this accessor mirror if any
|
||||||
//! @return nullptr if isMirror is false, the source otherwise.
|
//! @return nullptr if isMirror is false, the source otherwise.
|
||||||
std::shared_ptr<AMemory> getMirrored() override;
|
std::shared_ptr<AMemory> getMirrored() override;
|
||||||
|
|||||||
@@ -47,4 +47,9 @@ namespace ComSquare::Memory
|
|||||||
{
|
{
|
||||||
return this->_initial->getName();
|
return this->_initial->getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component RectangleShadow::getComponent()
|
||||||
|
{
|
||||||
|
return this->_initial->getComponent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -39,6 +39,8 @@ namespace ComSquare::Memory
|
|||||||
bool isMirror() override;
|
bool isMirror() override;
|
||||||
//! @brief Get the name of this accessor (used for debug purpose)
|
//! @brief Get the name of this accessor (used for debug purpose)
|
||||||
std::string getName() override;
|
std::string getName() override;
|
||||||
|
//! @brief Get the component of this accessor (used for debug purpose)
|
||||||
|
Component getComponent() override;
|
||||||
//! @brief Return the memory accessor this accessor mirror if any
|
//! @brief Return the memory accessor this accessor mirror if any
|
||||||
//! @return nullptr if isMirror is false, the source otherwise.
|
//! @return nullptr if isMirror is false, the source otherwise.
|
||||||
std::shared_ptr<AMemory> getMirrored() override;
|
std::shared_ptr<AMemory> getMirrored() override;
|
||||||
|
|||||||
@@ -249,4 +249,9 @@ namespace ComSquare::PPU
|
|||||||
{
|
{
|
||||||
return "PPU";
|
return "PPU";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component PPU::getComponent()
|
||||||
|
{
|
||||||
|
return Ppu;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -566,6 +566,8 @@ namespace ComSquare::PPU
|
|||||||
void write(uint24_t addr, uint8_t data) override;
|
void write(uint24_t addr, uint8_t data) override;
|
||||||
//! @brief Get the name of this accessor (used for debug purpose)
|
//! @brief Get the name of this accessor (used for debug purpose)
|
||||||
std::string getName() override;
|
std::string getName() override;
|
||||||
|
//! @brief Get the component of this accessor (used for debug purpose)
|
||||||
|
Component getComponent() override;
|
||||||
|
|
||||||
//! @brief Update the PPU of n cycles.
|
//! @brief Update the PPU of n cycles.
|
||||||
//! @param The number of cycles to update.
|
//! @param The number of cycles to update.
|
||||||
|
|||||||
+7
-1
@@ -9,8 +9,9 @@
|
|||||||
|
|
||||||
namespace ComSquare::Ram
|
namespace ComSquare::Ram
|
||||||
{
|
{
|
||||||
Ram::Ram(size_t size, std::string ramName)
|
Ram::Ram(size_t size, Component type, std::string ramName)
|
||||||
: _size(size),
|
: _size(size),
|
||||||
|
_ramType(type),
|
||||||
_ramName(std::move(ramName))
|
_ramName(std::move(ramName))
|
||||||
{
|
{
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
@@ -58,4 +59,9 @@ namespace ComSquare::Ram
|
|||||||
{
|
{
|
||||||
return this->_ramName;
|
return this->_ramName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component Ram::getComponent()
|
||||||
|
{
|
||||||
|
return this->_ramType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -15,11 +15,13 @@ namespace ComSquare::Ram
|
|||||||
uint8_t *_data;
|
uint8_t *_data;
|
||||||
//! @brief The size of the ram (iny bytes).
|
//! @brief The size of the ram (iny bytes).
|
||||||
size_t _size;
|
size_t _size;
|
||||||
|
//! @brief An id identifying the type of memory this is (for the debugger)
|
||||||
|
Component _ramType;
|
||||||
//! @brief The name of this ram.
|
//! @brief The name of this ram.
|
||||||
std::string _ramName;
|
std::string _ramName;
|
||||||
public:
|
public:
|
||||||
//! @brief Create a ram of a given size in bytes.
|
//! @brief Create a ram of a given size in bytes.
|
||||||
explicit Ram(size_t size, std::string ramName);
|
explicit Ram(size_t size, Component, std::string ramName);
|
||||||
//! @brief The ram can't be copied.
|
//! @brief The ram can't be copied.
|
||||||
Ram(const Ram &) = delete;
|
Ram(const Ram &) = delete;
|
||||||
//! @brief The ram can't be assigned.
|
//! @brief The ram can't be assigned.
|
||||||
@@ -46,6 +48,9 @@ namespace ComSquare::Ram
|
|||||||
//! @brief Get the name of this accessor (used for debug purpose)
|
//! @brief Get the name of this accessor (used for debug purpose)
|
||||||
std::string getName() override;
|
std::string getName() override;
|
||||||
|
|
||||||
|
//! @brief Get the component of this accessor (used for debug purpose)
|
||||||
|
Component getComponent() override;
|
||||||
|
|
||||||
//! @brief Get the size of the ram in bytes.
|
//! @brief Get the size of the ram in bytes.
|
||||||
size_t getSize();
|
size_t getSize();
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-2
@@ -17,8 +17,8 @@ namespace ComSquare
|
|||||||
SNES::SNES(const std::string &romPath, Renderer::IRenderer &renderer) :
|
SNES::SNES(const std::string &romPath, Renderer::IRenderer &renderer) :
|
||||||
_bus(std::make_shared<Memory::MemoryBus>()),
|
_bus(std::make_shared<Memory::MemoryBus>()),
|
||||||
cartridge(new Cartridge::Cartridge(romPath)),
|
cartridge(new Cartridge::Cartridge(romPath)),
|
||||||
wram(new Ram::Ram(16384, "WRam")),
|
wram(new Ram::Ram(16384, WRam, "WRam")),
|
||||||
sram(new Ram::Ram(this->cartridge->header.sramSize, "SRam")),
|
sram(new Ram::Ram(this->cartridge->header.sramSize, SRam, "SRam")),
|
||||||
apuRam(new APU::MemoryMap()),
|
apuRam(new APU::MemoryMap()),
|
||||||
cpu(new CPU::CPU(this->_bus, cartridge->header)),
|
cpu(new CPU::CPU(this->_bus, cartridge->header)),
|
||||||
ppu(new PPU::PPU(renderer)),
|
ppu(new PPU::PPU(renderer)),
|
||||||
|
|||||||
Reference in New Issue
Block a user