initial cpu structure :thonk:
Signed-off-by: Amneesh Singh <natto@weirdnatto.in>
This commit is contained in:
		
							
								
								
									
										39
									
								
								src/bits.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/bits.hh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <concepts>
 | 
			
		||||
#include <limits>
 | 
			
		||||
 | 
			
		||||
using std::size_t;
 | 
			
		||||
 | 
			
		||||
template<std::integral Int>
 | 
			
		||||
inline bool
 | 
			
		||||
get_nth_bit(Int num, size_t n) {
 | 
			
		||||
    return (1 && (num >> n));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<std::integral Int>
 | 
			
		||||
inline void
 | 
			
		||||
set_nth_bit(Int& num, size_t n) {
 | 
			
		||||
    num |= (1 << n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<std::integral Int>
 | 
			
		||||
inline void
 | 
			
		||||
rst_nth_bit(Int& num, size_t n) {
 | 
			
		||||
    num &= ~(1 << n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<std::integral Int>
 | 
			
		||||
inline void
 | 
			
		||||
chg_nth_bit(Int& num, size_t n, bool x) {
 | 
			
		||||
    num ^= (num ^ -x) & 1 << n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// read range of bits from start to end inclusive
 | 
			
		||||
template<std::unsigned_integral Int>
 | 
			
		||||
inline Int
 | 
			
		||||
get_bit_range(Int& num, size_t start, size_t end) {
 | 
			
		||||
    // NOTE: we do not require -1 if it is a signed integral (which it is not)
 | 
			
		||||
    Int left = std::numeric_limits<Int>::digits - 1 - end;
 | 
			
		||||
    return num << left >> (left + start);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/bus.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/bus.hh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
class Bus {
 | 
			
		||||
    std::vector<uint8_t> data;
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    Bus() = default;
 | 
			
		||||
    Bus(std::istream& ifile)
 | 
			
		||||
      : data(std::istreambuf_iterator<char>(ifile),
 | 
			
		||||
             std::istreambuf_iterator<char>()) {}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										61
									
								
								src/cpu/arm/insruction.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/cpu/arm/insruction.hh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
enum class ArmInstructionFormat {
 | 
			
		||||
    DataProcessingAndFsrTransfer,
 | 
			
		||||
    Multiply,
 | 
			
		||||
    MultiplyLong,
 | 
			
		||||
    SingleDataSwap,
 | 
			
		||||
    BranchAndExchange,
 | 
			
		||||
    HalfwordDataTransferRegisterOffset,
 | 
			
		||||
    HalfwordDataTransferImmediateOffset,
 | 
			
		||||
    SingleDataTransfer,
 | 
			
		||||
    Undefined,
 | 
			
		||||
    BlockDataTransfer,
 | 
			
		||||
    Branch,
 | 
			
		||||
    CoprocessorDataTransfer,
 | 
			
		||||
    CoprocessorDataOperation,
 | 
			
		||||
    CoprocessorRegisterTransfer,
 | 
			
		||||
    SoftwareInterrupt
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum class Shift {
 | 
			
		||||
    LSL = 0b00,
 | 
			
		||||
    LSR = 0b01,
 | 
			
		||||
    ASR = 0b10,
 | 
			
		||||
    ROR = 0b11
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										2
									
								
								src/cpu/arm/meson.build
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/cpu/arm/meson.build
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
lib_sources += files(
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										106
									
								
								src/cpu/cpu.cc
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								src/cpu/cpu.cc
									
									
									
									
									
								
							@@ -1,6 +1,104 @@
 | 
			
		||||
#include "cpu.hh"
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include "cpu/utility.hh"
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
 | 
			
		||||
namespace cpu {
 | 
			
		||||
void run() { std::cout << "Hello from inside the CPU" << std::endl; }
 | 
			
		||||
} // namespace cpu
 | 
			
		||||
/* change modes */
 | 
			
		||||
void
 | 
			
		||||
Cpu::chg_mode(Mode from, Mode to) {
 | 
			
		||||
    if (from == to)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
/* TODO: replace visible registers with view once I understand how to
 | 
			
		||||
 * concatenate views */
 | 
			
		||||
#define STORE_BANKED(mode, MODE)                                               \
 | 
			
		||||
    std::copy(gpr + GPR_##MODE##_BANKED_FIRST,                                 \
 | 
			
		||||
              gpr + GPR_##MODE##_BANKED_FIRST + GPR_##MODE##_BANKED_COUNT,     \
 | 
			
		||||
              gpr_banked.mode)
 | 
			
		||||
 | 
			
		||||
    switch (from) {
 | 
			
		||||
        case Mode::Fiq:
 | 
			
		||||
            STORE_BANKED(fiq, FIQ);
 | 
			
		||||
            spsr_banked.fiq = spsr;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Supervisor:
 | 
			
		||||
            STORE_BANKED(svc, SVC);
 | 
			
		||||
            spsr_banked.svc = spsr;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Abort:
 | 
			
		||||
            STORE_BANKED(abt, ABT);
 | 
			
		||||
            spsr_banked.abt = spsr;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Irq:
 | 
			
		||||
            STORE_BANKED(irq, IRQ);
 | 
			
		||||
            spsr_banked.irq = spsr;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Undefined:
 | 
			
		||||
            STORE_BANKED(und, UND);
 | 
			
		||||
            spsr_banked.und = spsr;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::User:
 | 
			
		||||
        case Mode::System:
 | 
			
		||||
            STORE_BANKED(old, SYS_USR);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#define RESTORE_BANKED(mode, MODE)                                             \
 | 
			
		||||
    std::copy(gpr_banked.mode,                                                 \
 | 
			
		||||
              gpr_banked.mode + GPR_##MODE##_BANKED_COUNT,                     \
 | 
			
		||||
              gpr + GPR_##MODE##_BANKED_FIRST)
 | 
			
		||||
 | 
			
		||||
    switch (to) {
 | 
			
		||||
        case Mode::Fiq:
 | 
			
		||||
            RESTORE_BANKED(fiq, FIQ);
 | 
			
		||||
            spsr = spsr_banked.fiq;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Supervisor:
 | 
			
		||||
            RESTORE_BANKED(svc, SVC);
 | 
			
		||||
            spsr = spsr_banked.svc;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Abort:
 | 
			
		||||
            RESTORE_BANKED(abt, ABT);
 | 
			
		||||
            spsr = spsr_banked.abt;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Irq:
 | 
			
		||||
            RESTORE_BANKED(irq, IRQ);
 | 
			
		||||
            spsr = spsr_banked.irq;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::Undefined:
 | 
			
		||||
            RESTORE_BANKED(und, UND);
 | 
			
		||||
            spsr = spsr_banked.und;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case Mode::User:
 | 
			
		||||
        case Mode::System:
 | 
			
		||||
            STORE_BANKED(old, SYS_USR);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#undef RESTORE_BANKED
 | 
			
		||||
 | 
			
		||||
    cpsr.set_mode(to);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//  set register
 | 
			
		||||
inline uint32_t&
 | 
			
		||||
Cpu::operator[](size_t idx) {
 | 
			
		||||
    // avoid unneeded complexity like index checks
 | 
			
		||||
    return gpr[idx];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// get register
 | 
			
		||||
inline const uint32_t&
 | 
			
		||||
Cpu::operator[](size_t idx) const {
 | 
			
		||||
    return gpr[idx];
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,77 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#define ABSOLUTE "MADarchod"
 | 
			
		||||
#include "bus.hh"
 | 
			
		||||
#include "psr.hh"
 | 
			
		||||
 | 
			
		||||
namespace cpu {
 | 
			
		||||
void run();
 | 
			
		||||
}
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
using std::size_t;
 | 
			
		||||
 | 
			
		||||
static constexpr size_t GPR_VISIBLE_COUNT = 16;
 | 
			
		||||
 | 
			
		||||
static constexpr size_t GPR_FIQ_BANKED_FIRST = 8;
 | 
			
		||||
static constexpr size_t GPR_FIQ_BANKED_COUNT = 7;
 | 
			
		||||
 | 
			
		||||
static constexpr size_t GPR_SVC_BANKED_FIRST = 13;
 | 
			
		||||
static constexpr size_t GPR_SVC_BANKED_COUNT = 2;
 | 
			
		||||
 | 
			
		||||
static constexpr size_t GPR_ABT_BANKED_FIRST = 13;
 | 
			
		||||
static constexpr size_t GPR_ABT_BANKED_COUNT = 2;
 | 
			
		||||
 | 
			
		||||
static constexpr size_t GPR_IRQ_BANKED_FIRST = 13;
 | 
			
		||||
static constexpr size_t GPR_IRQ_BANKED_COUNT = 2;
 | 
			
		||||
 | 
			
		||||
static constexpr size_t GPR_UND_BANKED_FIRST = 13;
 | 
			
		||||
static constexpr size_t GPR_UND_BANKED_COUNT = 2;
 | 
			
		||||
 | 
			
		||||
static constexpr size_t GPR_SYS_USR_BANKED_FIRST = 8;
 | 
			
		||||
static constexpr size_t GPR_SYS_USR_BANKED_COUNT = 7;
 | 
			
		||||
 | 
			
		||||
struct _GprBanked {
 | 
			
		||||
    uint32_t fiq[GPR_FIQ_BANKED_COUNT];
 | 
			
		||||
    uint32_t svc[GPR_SVC_BANKED_COUNT];
 | 
			
		||||
    uint32_t abt[GPR_ABT_BANKED_COUNT];
 | 
			
		||||
    uint32_t irq[GPR_IRQ_BANKED_COUNT];
 | 
			
		||||
    uint32_t und[GPR_UND_BANKED_COUNT];
 | 
			
		||||
 | 
			
		||||
    /* visible registers before the mode switch */
 | 
			
		||||
    uint32_t old[GPR_SYS_USR_BANKED_COUNT];
 | 
			
		||||
};
 | 
			
		||||
typedef struct _GprBanked GprBanked;
 | 
			
		||||
 | 
			
		||||
struct _SpsrBanked {
 | 
			
		||||
    Psr fiq;
 | 
			
		||||
    Psr svc;
 | 
			
		||||
    Psr abt;
 | 
			
		||||
    Psr irq;
 | 
			
		||||
    Psr und;
 | 
			
		||||
};
 | 
			
		||||
typedef struct _SpsrBanked SpsrBanked;
 | 
			
		||||
 | 
			
		||||
class Cpu {
 | 
			
		||||
    uint32_t gpr[GPR_VISIBLE_COUNT]; // general purpose registers
 | 
			
		||||
    GprBanked gpr_banked;            // banked general purpose registers
 | 
			
		||||
    SpsrBanked spsr_banked;          // banked saved program status registers
 | 
			
		||||
    Psr cpsr;                        // current program status register
 | 
			
		||||
    Psr spsr;                        // status program status register
 | 
			
		||||
    Bus bus;
 | 
			
		||||
 | 
			
		||||
    void chg_mode(Mode from, Mode to);
 | 
			
		||||
 | 
			
		||||
    uint32_t& operator[](size_t idx);
 | 
			
		||||
    const uint32_t& operator[](size_t idx) const;
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    Cpu(Bus bus)
 | 
			
		||||
      : gpr(0)
 | 
			
		||||
      , gpr_banked({ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } })
 | 
			
		||||
      , spsr_banked({ 0, 0, 0, 0, 0 })
 | 
			
		||||
      , cpsr(0)
 | 
			
		||||
      , spsr(0)
 | 
			
		||||
      , bus(bus) {
 | 
			
		||||
        cpsr.set_mode(Mode::System);
 | 
			
		||||
        cpsr.set_irq_disabled(true);
 | 
			
		||||
        cpsr.set_fiq_disabled(true);
 | 
			
		||||
        cpsr.set_state(State::Arm);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
lib_sources += files(
 | 
			
		||||
  'cpu.cc'
 | 
			
		||||
)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
subdir('arm')
 | 
			
		||||
							
								
								
									
										55
									
								
								src/cpu/psr.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/cpu/psr.hh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "bits.hh"
 | 
			
		||||
#include "utility.hh"
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
 | 
			
		||||
static constexpr uint32_t PSR_CLEAR_RESERVED = 0xf00000ff;
 | 
			
		||||
static constexpr uint32_t PSR_CLEAR_MODE     = 0x0b00000;
 | 
			
		||||
 | 
			
		||||
class Psr {
 | 
			
		||||
    uint32_t psr;
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
    // clear the reserved bits i.e, [8:27]
 | 
			
		||||
    Psr(uint32_t raw) { psr = raw & PSR_CLEAR_RESERVED; }
 | 
			
		||||
 | 
			
		||||
    // Mode : [4:0]
 | 
			
		||||
    Mode mode() const { return static_cast<Mode>(psr & ~PSR_CLEAR_MODE); }
 | 
			
		||||
    void set_mode(Mode mode) {
 | 
			
		||||
        psr &= PSR_CLEAR_MODE;
 | 
			
		||||
        psr |= static_cast<uint32_t>(mode);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // State : [5]
 | 
			
		||||
    bool state() const { return get_nth_bit(psr, 5); }
 | 
			
		||||
    void set_state(State state) {
 | 
			
		||||
        chg_nth_bit(psr, 5, static_cast<bool>(state));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#define GET_SET_NTH_BIT_FUNCTIONS(name, n)                                     \
 | 
			
		||||
    bool name() const { return get_nth_bit(psr, n); }                          \
 | 
			
		||||
    void set_##name(bool val) { chg_nth_bit(psr, n, val); }
 | 
			
		||||
 | 
			
		||||
    // FIQ disable : [6]
 | 
			
		||||
    GET_SET_NTH_BIT_FUNCTIONS(fiq_disabled, 6)
 | 
			
		||||
 | 
			
		||||
    // IRQ disable : [7]
 | 
			
		||||
    GET_SET_NTH_BIT_FUNCTIONS(irq_disabled, 7)
 | 
			
		||||
 | 
			
		||||
    // Reserved bits : [27:8]
 | 
			
		||||
 | 
			
		||||
    // Overflow flag : [28]
 | 
			
		||||
    GET_SET_NTH_BIT_FUNCTIONS(v, 28);
 | 
			
		||||
 | 
			
		||||
    // Carry flag : [29]
 | 
			
		||||
    GET_SET_NTH_BIT_FUNCTIONS(c, 29);
 | 
			
		||||
 | 
			
		||||
    // Zero flag : [30]
 | 
			
		||||
    GET_SET_NTH_BIT_FUNCTIONS(z, 30);
 | 
			
		||||
 | 
			
		||||
    // Negative flag : [30]
 | 
			
		||||
    GET_SET_NTH_BIT_FUNCTIONS(n, 31);
 | 
			
		||||
 | 
			
		||||
#undef GET_SET_NTH_BIT_FUNCTIONS
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										17
									
								
								src/cpu/utility.hh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/cpu/utility.hh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										24
									
								
								src/emulator.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/emulator.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
#include "emulator.hh"
 | 
			
		||||
#include "bus.hh"
 | 
			
		||||
#include "cpu/cpu.hh"
 | 
			
		||||
#include <fstream>
 | 
			
		||||
 | 
			
		||||
namespace emulator {
 | 
			
		||||
void
 | 
			
		||||
run(std::ifstream& ifile) {
 | 
			
		||||
    Bus bus(ifile);
 | 
			
		||||
    Cpu cpu(bus);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
run(std::string filepath) {
 | 
			
		||||
    std::ifstream ifile(filepath, std::ios::in | std::ios::binary);
 | 
			
		||||
 | 
			
		||||
    if (!ifile.is_open()) {
 | 
			
		||||
        throw std::ios::failure("No such file exists", std::error_code());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    run(ifile);
 | 
			
		||||
    ifile.close();
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
#include "matar.hh"
 | 
			
		||||
#include "cpu/cpu.hh"
 | 
			
		||||
 | 
			
		||||
int run() {
 | 
			
		||||
    cpu::run();
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
lib_sources = files(
 | 
			
		||||
  'matar.cc'
 | 
			
		||||
  'emulator.cc'
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
subdir('cpu')
 | 
			
		||||
@@ -7,6 +7,8 @@ subdir('cpu')
 | 
			
		||||
lib = library(
 | 
			
		||||
  meson.project_name(),
 | 
			
		||||
  lib_sources,
 | 
			
		||||
  include_directories: includes,
 | 
			
		||||
  install : true
 | 
			
		||||
)
 | 
			
		||||
  include_directories: inc,
 | 
			
		||||
  install: true
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
import('pkgconfig').generate(lib)
 | 
			
		||||
		Reference in New Issue
	
	Block a user