restructure: get rid of cpu/utility
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
		@@ -1,74 +1,7 @@
 | 
			
		||||
#include "utility.hh"
 | 
			
		||||
#include "alu.hh"
 | 
			
		||||
#include "util/bits.hh"
 | 
			
		||||
#include <bit>
 | 
			
		||||
 | 
			
		||||
namespace matar {
 | 
			
		||||
namespace arm {
 | 
			
		||||
std::ostream&
 | 
			
		||||
operator<<(std::ostream& os, const Condition cond) {
 | 
			
		||||
 | 
			
		||||
#define CASE(cond)                                                             \
 | 
			
		||||
    case Condition::cond:                                                      \
 | 
			
		||||
        os << #cond;                                                           \
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    switch (cond) {
 | 
			
		||||
        CASE(EQ)
 | 
			
		||||
        CASE(NE)
 | 
			
		||||
        CASE(CS)
 | 
			
		||||
        CASE(CC)
 | 
			
		||||
        CASE(MI)
 | 
			
		||||
        CASE(PL)
 | 
			
		||||
        CASE(VS)
 | 
			
		||||
        CASE(VC)
 | 
			
		||||
        CASE(HI)
 | 
			
		||||
        CASE(LS)
 | 
			
		||||
        CASE(GE)
 | 
			
		||||
        CASE(LT)
 | 
			
		||||
        CASE(GT)
 | 
			
		||||
        CASE(LE)
 | 
			
		||||
        case Condition::AL: {
 | 
			
		||||
            // empty
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#undef CASE
 | 
			
		||||
 | 
			
		||||
    return os;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::ostream&
 | 
			
		||||
operator<<(std::ostream& os, const OpCode opcode) {
 | 
			
		||||
 | 
			
		||||
#define CASE(opcode)                                                           \
 | 
			
		||||
    case OpCode::opcode:                                                       \
 | 
			
		||||
        os << #opcode;                                                         \
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    switch (opcode) {
 | 
			
		||||
        CASE(AND)
 | 
			
		||||
        CASE(EOR)
 | 
			
		||||
        CASE(SUB)
 | 
			
		||||
        CASE(RSB)
 | 
			
		||||
        CASE(ADD)
 | 
			
		||||
        CASE(ADC)
 | 
			
		||||
        CASE(SBC)
 | 
			
		||||
        CASE(RSC)
 | 
			
		||||
        CASE(TST)
 | 
			
		||||
        CASE(TEQ)
 | 
			
		||||
        CASE(CMP)
 | 
			
		||||
        CASE(CMN)
 | 
			
		||||
        CASE(ORR)
 | 
			
		||||
        CASE(MOV)
 | 
			
		||||
        CASE(BIC)
 | 
			
		||||
        CASE(MVN)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#undef CASE
 | 
			
		||||
 | 
			
		||||
    return os;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t
 | 
			
		||||
eval_shift(ShiftType shift_type, uint32_t value, uint8_t amount, bool& carry) {
 | 
			
		||||
    uint32_t eval = 0;
 | 
			
		||||
@@ -136,4 +69,3 @@ operator<<(std::ostream& os, const ShiftType shift_type) {
 | 
			
		||||
    return os;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								src/cpu/alu.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/cpu/alu.hh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <fmt/ostream.h>
 | 
			
		||||
 | 
			
		||||
namespace matar {
 | 
			
		||||
enum class ShiftType {
 | 
			
		||||
    LSL = 0b00,
 | 
			
		||||
    LSR = 0b01,
 | 
			
		||||
    ASR = 0b10,
 | 
			
		||||
    ROR = 0b11
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ShiftData {
 | 
			
		||||
    ShiftType type;
 | 
			
		||||
    bool immediate;
 | 
			
		||||
    uint8_t operand;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Shift {
 | 
			
		||||
    uint8_t rm;
 | 
			
		||||
    ShiftData data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
uint32_t
 | 
			
		||||
eval_shift(ShiftType shift_type, uint32_t value, uint8_t amount, bool& carry);
 | 
			
		||||
 | 
			
		||||
// https://fmt.dev/dev/api.html#std-ostream-support
 | 
			
		||||
std::ostream&
 | 
			
		||||
operator<<(std::ostream& os, const ShiftType cond);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace fmt {
 | 
			
		||||
template<>
 | 
			
		||||
struct formatter<matar::ShiftType> : ostream_formatter {};
 | 
			
		||||
}
 | 
			
		||||
@@ -7,7 +7,7 @@ using namespace logger;
 | 
			
		||||
namespace matar {
 | 
			
		||||
void
 | 
			
		||||
CpuImpl::exec_arm(const arm::Instruction instruction) {
 | 
			
		||||
    arm::Condition cond       = instruction.condition;
 | 
			
		||||
    Condition cond            = instruction.condition;
 | 
			
		||||
    arm::InstructionData data = instruction.data;
 | 
			
		||||
 | 
			
		||||
    debug(cpsr.condition(cond));
 | 
			
		||||
@@ -387,6 +387,8 @@ CpuImpl::exec_arm(const arm::Instruction instruction) {
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        [this, pc_error](DataProcessing& data) {
 | 
			
		||||
            using OpCode = DataProcessing::OpCode;
 | 
			
		||||
 | 
			
		||||
            uint32_t op_1 = gpr[data.rn];
 | 
			
		||||
            uint32_t op_2 = 0;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
#include "instruction.hh"
 | 
			
		||||
#include "cpu/utility.hh"
 | 
			
		||||
#include "util/bits.hh"
 | 
			
		||||
#include <iterator>
 | 
			
		||||
 | 
			
		||||
@@ -153,6 +152,8 @@ Instruction::Instruction(uint32_t insn)
 | 
			
		||||
 | 
			
		||||
        // Data Processing
 | 
			
		||||
    } else if ((insn & 0x0C000000) == 0x00000000) {
 | 
			
		||||
        using OpCode = DataProcessing::OpCode;
 | 
			
		||||
 | 
			
		||||
        uint8_t rd    = bit_range(insn, 12, 15);
 | 
			
		||||
        uint8_t rn    = bit_range(insn, 16, 19);
 | 
			
		||||
        bool set      = get_bit(insn, 20);
 | 
			
		||||
@@ -420,6 +421,8 @@ Instruction::disassemble() {
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        [this](DataProcessing& data) {
 | 
			
		||||
            using OpCode = DataProcessing::OpCode;
 | 
			
		||||
 | 
			
		||||
            std::string op_2;
 | 
			
		||||
 | 
			
		||||
            if (const uint32_t* operand =
 | 
			
		||||
@@ -496,5 +499,37 @@ Instruction::disassemble() {
 | 
			
		||||
        [](auto) { return std::string("unknown instruction"); } },
 | 
			
		||||
      data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::ostream&
 | 
			
		||||
operator<<(std::ostream& os, const DataProcessing::OpCode opcode) {
 | 
			
		||||
 | 
			
		||||
#define CASE(opcode)                                                           \
 | 
			
		||||
    case DataProcessing::OpCode::opcode:                                       \
 | 
			
		||||
        os << #opcode;                                                         \
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    switch (opcode) {
 | 
			
		||||
        CASE(AND)
 | 
			
		||||
        CASE(EOR)
 | 
			
		||||
        CASE(SUB)
 | 
			
		||||
        CASE(RSB)
 | 
			
		||||
        CASE(ADD)
 | 
			
		||||
        CASE(ADC)
 | 
			
		||||
        CASE(SBC)
 | 
			
		||||
        CASE(RSC)
 | 
			
		||||
        CASE(TST)
 | 
			
		||||
        CASE(TEQ)
 | 
			
		||||
        CASE(CMP)
 | 
			
		||||
        CASE(CMN)
 | 
			
		||||
        CASE(ORR)
 | 
			
		||||
        CASE(MOV)
 | 
			
		||||
        CASE(BIC)
 | 
			
		||||
        CASE(MVN)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#undef CASE
 | 
			
		||||
 | 
			
		||||
    return os;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,13 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "cpu/utility.hh"
 | 
			
		||||
#include "cpu/alu.hh"
 | 
			
		||||
#include "cpu/psr.hh"
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <fmt/ostream.h>
 | 
			
		||||
#include <variant>
 | 
			
		||||
 | 
			
		||||
namespace matar {
 | 
			
		||||
namespace arm {
 | 
			
		||||
 | 
			
		||||
template<class... Ts>
 | 
			
		||||
struct overloaded : Ts... {
 | 
			
		||||
    using Ts::operator()...;
 | 
			
		||||
@@ -10,8 +15,6 @@ struct overloaded : Ts... {
 | 
			
		||||
template<class... Ts>
 | 
			
		||||
overloaded(Ts...) -> overloaded<Ts...>;
 | 
			
		||||
 | 
			
		||||
namespace matar {
 | 
			
		||||
namespace arm {
 | 
			
		||||
static constexpr size_t INSTRUCTION_SIZE = 4;
 | 
			
		||||
 | 
			
		||||
struct BranchAndExchange {
 | 
			
		||||
@@ -84,6 +87,25 @@ struct BlockDataTransfer {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct DataProcessing {
 | 
			
		||||
    enum class OpCode {
 | 
			
		||||
        AND = 0b0000,
 | 
			
		||||
        EOR = 0b0001,
 | 
			
		||||
        SUB = 0b0010,
 | 
			
		||||
        RSB = 0b0011,
 | 
			
		||||
        ADD = 0b0100,
 | 
			
		||||
        ADC = 0b0101,
 | 
			
		||||
        SBC = 0b0110,
 | 
			
		||||
        RSC = 0b0111,
 | 
			
		||||
        TST = 0b1000,
 | 
			
		||||
        TEQ = 0b1001,
 | 
			
		||||
        CMP = 0b1010,
 | 
			
		||||
        CMN = 0b1011,
 | 
			
		||||
        ORR = 0b1100,
 | 
			
		||||
        MOV = 0b1101,
 | 
			
		||||
        BIC = 0b1110,
 | 
			
		||||
        MVN = 0b1111
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    std::variant<Shift, uint32_t> operand;
 | 
			
		||||
    uint8_t rd;
 | 
			
		||||
    uint8_t rn;
 | 
			
		||||
@@ -166,5 +188,13 @@ struct Instruction {
 | 
			
		||||
 | 
			
		||||
    std::string disassemble();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::ostream&
 | 
			
		||||
operator<<(std::ostream& os, const DataProcessing::OpCode cond);
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace fmt {
 | 
			
		||||
template<>
 | 
			
		||||
struct formatter<matar::arm::DataProcessing::OpCode> : ostream_formatter {};
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
#include "cpu-impl.hh"
 | 
			
		||||
#include "util/bits.hh"
 | 
			
		||||
#include "util/log.hh"
 | 
			
		||||
#include "utility.hh"
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ lib_sources += files(
 | 
			
		||||
  'cpu-impl.cc',
 | 
			
		||||
  'cpu.cc',
 | 
			
		||||
  'psr.cc',
 | 
			
		||||
  'utility.cc'
 | 
			
		||||
  'alu.cc'
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
subdir('arm')
 | 
			
		||||
@@ -60,9 +60,7 @@ GET_SET_NTH_BIT_FUNCTIONS(n, 31);
 | 
			
		||||
#undef GET_SET_NTH_BIT_FUNCTIONS
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
Psr::condition(arm::Condition cond) const {
 | 
			
		||||
    using arm::Condition;
 | 
			
		||||
 | 
			
		||||
Psr::condition(Condition cond) const {
 | 
			
		||||
    switch (cond) {
 | 
			
		||||
        case Condition::EQ:
 | 
			
		||||
            return z();
 | 
			
		||||
@@ -93,9 +91,42 @@ Psr::condition(arm::Condition cond) const {
 | 
			
		||||
        case Condition::LE:
 | 
			
		||||
            return z() || (n() != v());
 | 
			
		||||
        case Condition::AL:
 | 
			
		||||
            return true;
 | 
			
		||||
            return true && state() == State::Arm;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::ostream&
 | 
			
		||||
operator<<(std::ostream& os, const Condition cond) {
 | 
			
		||||
 | 
			
		||||
#define CASE(cond)                                                             \
 | 
			
		||||
    case Condition::cond:                                                      \
 | 
			
		||||
        os << #cond;                                                           \
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    switch (cond) {
 | 
			
		||||
        CASE(EQ)
 | 
			
		||||
        CASE(NE)
 | 
			
		||||
        CASE(CS)
 | 
			
		||||
        CASE(CC)
 | 
			
		||||
        CASE(MI)
 | 
			
		||||
        CASE(PL)
 | 
			
		||||
        CASE(VS)
 | 
			
		||||
        CASE(VC)
 | 
			
		||||
        CASE(HI)
 | 
			
		||||
        CASE(LS)
 | 
			
		||||
        CASE(GE)
 | 
			
		||||
        CASE(LT)
 | 
			
		||||
        CASE(GT)
 | 
			
		||||
        CASE(LE)
 | 
			
		||||
        case Condition::AL: {
 | 
			
		||||
            // empty
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#undef CASE
 | 
			
		||||
 | 
			
		||||
    return os;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,43 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "arm/instruction.hh"
 | 
			
		||||
#include "utility.hh"
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <fmt/ostream.h>
 | 
			
		||||
 | 
			
		||||
namespace matar {
 | 
			
		||||
enum class Mode {
 | 
			
		||||
    /* M[4:0] in PSR */
 | 
			
		||||
    User       = 0b10000,
 | 
			
		||||
    Fiq        = 0b10001,
 | 
			
		||||
    Irq        = 0b10010,
 | 
			
		||||
    Supervisor = 0b10011,
 | 
			
		||||
    Abort      = 0b10111,
 | 
			
		||||
    Undefined  = 0b11011,
 | 
			
		||||
    System     = 0b11111,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum class State {
 | 
			
		||||
    Arm   = 0,
 | 
			
		||||
    Thumb = 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum class Condition {
 | 
			
		||||
    EQ = 0b0000,
 | 
			
		||||
    NE = 0b0001,
 | 
			
		||||
    CS = 0b0010,
 | 
			
		||||
    CC = 0b0011,
 | 
			
		||||
    MI = 0b0100,
 | 
			
		||||
    PL = 0b0101,
 | 
			
		||||
    VS = 0b0110,
 | 
			
		||||
    VC = 0b0111,
 | 
			
		||||
    HI = 0b1000,
 | 
			
		||||
    LS = 0b1001,
 | 
			
		||||
    GE = 0b1010,
 | 
			
		||||
    LT = 0b1011,
 | 
			
		||||
    GT = 0b1100,
 | 
			
		||||
    LE = 0b1101,
 | 
			
		||||
    AL = 0b1110
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Psr {
 | 
			
		||||
  public:
 | 
			
		||||
    // clear the reserved bits i.e, [8:27]
 | 
			
		||||
@@ -47,7 +80,7 @@ class Psr {
 | 
			
		||||
 | 
			
		||||
#undef GET_SET_NTH_BIT_FUNCTIONS
 | 
			
		||||
 | 
			
		||||
    bool condition(arm::Condition cond) const;
 | 
			
		||||
    bool condition(Condition cond) const;
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    static constexpr uint32_t PSR_CLEAR_RESERVED = 0xF00000FF;
 | 
			
		||||
@@ -55,4 +88,13 @@ class Psr {
 | 
			
		||||
 | 
			
		||||
    uint32_t psr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// https://fmt.dev/dev/api.html#std-ostream-support
 | 
			
		||||
std::ostream&
 | 
			
		||||
operator<<(std::ostream& os, const Condition cond);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace fmt {
 | 
			
		||||
template<>
 | 
			
		||||
struct formatter<matar::Condition> : ostream_formatter {};
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,103 +0,0 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <fmt/ostream.h>
 | 
			
		||||
#include <ostream>
 | 
			
		||||
 | 
			
		||||
static constexpr size_t THUMB_INSTRUCTION_SIZE = 2;
 | 
			
		||||
 | 
			
		||||
namespace matar {
 | 
			
		||||
enum class Mode {
 | 
			
		||||
    /* M[4:0] in PSR */
 | 
			
		||||
    User       = 0b10000,
 | 
			
		||||
    Fiq        = 0b10001,
 | 
			
		||||
    Irq        = 0b10010,
 | 
			
		||||
    Supervisor = 0b10011,
 | 
			
		||||
    Abort      = 0b10111,
 | 
			
		||||
    Undefined  = 0b11011,
 | 
			
		||||
    System     = 0b11111,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum class State {
 | 
			
		||||
    Arm   = 0,
 | 
			
		||||
    Thumb = 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace arm {
 | 
			
		||||
enum class Condition {
 | 
			
		||||
    EQ = 0b0000,
 | 
			
		||||
    NE = 0b0001,
 | 
			
		||||
    CS = 0b0010,
 | 
			
		||||
    CC = 0b0011,
 | 
			
		||||
    MI = 0b0100,
 | 
			
		||||
    PL = 0b0101,
 | 
			
		||||
    VS = 0b0110,
 | 
			
		||||
    VC = 0b0111,
 | 
			
		||||
    HI = 0b1000,
 | 
			
		||||
    LS = 0b1001,
 | 
			
		||||
    GE = 0b1010,
 | 
			
		||||
    LT = 0b1011,
 | 
			
		||||
    GT = 0b1100,
 | 
			
		||||
    LE = 0b1101,
 | 
			
		||||
    AL = 0b1110
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// https://fmt.dev/dev/api.html#std-ostream-support
 | 
			
		||||
std::ostream&
 | 
			
		||||
operator<<(std::ostream& os, const Condition cond);
 | 
			
		||||
 | 
			
		||||
enum class OpCode {
 | 
			
		||||
    AND = 0b0000,
 | 
			
		||||
    EOR = 0b0001,
 | 
			
		||||
    SUB = 0b0010,
 | 
			
		||||
    RSB = 0b0011,
 | 
			
		||||
    ADD = 0b0100,
 | 
			
		||||
    ADC = 0b0101,
 | 
			
		||||
    SBC = 0b0110,
 | 
			
		||||
    RSC = 0b0111,
 | 
			
		||||
    TST = 0b1000,
 | 
			
		||||
    TEQ = 0b1001,
 | 
			
		||||
    CMP = 0b1010,
 | 
			
		||||
    CMN = 0b1011,
 | 
			
		||||
    ORR = 0b1100,
 | 
			
		||||
    MOV = 0b1101,
 | 
			
		||||
    BIC = 0b1110,
 | 
			
		||||
    MVN = 0b1111
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// https://fmt.dev/dev/api.html#std-ostream-support
 | 
			
		||||
std::ostream&
 | 
			
		||||
operator<<(std::ostream& os, const OpCode cond);
 | 
			
		||||
 | 
			
		||||
enum class ShiftType {
 | 
			
		||||
    LSL = 0b00,
 | 
			
		||||
    LSR = 0b01,
 | 
			
		||||
    ASR = 0b10,
 | 
			
		||||
    ROR = 0b11
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ShiftData {
 | 
			
		||||
    ShiftType type;
 | 
			
		||||
    bool immediate;
 | 
			
		||||
    uint8_t operand;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Shift {
 | 
			
		||||
    uint8_t rm;
 | 
			
		||||
    ShiftData data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
uint32_t
 | 
			
		||||
eval_shift(ShiftType shift_type, uint32_t value, uint8_t amount, bool& carry);
 | 
			
		||||
 | 
			
		||||
// https://fmt.dev/dev/api.html#std-ostream-support
 | 
			
		||||
std::ostream&
 | 
			
		||||
operator<<(std::ostream& os, const ShiftType cond);
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
struct fmt::formatter<matar::arm::Condition> : ostream_formatter {};
 | 
			
		||||
template<>
 | 
			
		||||
struct fmt::formatter<matar::arm::OpCode> : ostream_formatter {};
 | 
			
		||||
template<>
 | 
			
		||||
struct fmt::formatter<matar::arm::ShiftType> : ostream_formatter {};
 | 
			
		||||
		Reference in New Issue
	
	Block a user