- [NEW] gbscreen ara accedeix a la hram i vram directament
- [NEW] Afegida al makefile opció per a profilechar
This commit is contained in:
3
Makefile
3
Makefile
@@ -12,3 +12,6 @@ debug1: compile
|
|||||||
|
|
||||||
release:
|
release:
|
||||||
g++ -O3 *.cpp -lSDL2 -o gb
|
g++ -O3 *.cpp -lSDL2 -o gb
|
||||||
|
|
||||||
|
profile:
|
||||||
|
g++ -g *.cpp -lSDL2 -o gb -pg
|
||||||
|
|||||||
131
gbscreen.cpp
131
gbscreen.cpp
@@ -11,6 +11,11 @@
|
|||||||
|
|
||||||
namespace gbscreen
|
namespace gbscreen
|
||||||
{
|
{
|
||||||
|
struct oam_entry_t
|
||||||
|
{
|
||||||
|
uint8_t y, x, tile, attr;
|
||||||
|
};
|
||||||
|
|
||||||
uint32_t palette[4] = {
|
uint32_t palette[4] = {
|
||||||
//0xFFFFFF, 0xFF0000, 0x00FF00, 0x0000FF
|
//0xFFFFFF, 0xFF0000, 0x00FF00, 0x0000FF
|
||||||
0xFFFFFF, 0xAAAAAA, 0x555555, 0x000000
|
0xFFFFFF, 0xAAAAAA, 0x555555, 0x000000
|
||||||
@@ -35,11 +40,35 @@ namespace gbscreen
|
|||||||
uint32_t t_screen = 0;
|
uint32_t t_screen = 0;
|
||||||
|
|
||||||
uint8_t gb_pixels[160*144];
|
uint8_t gb_pixels[160*144];
|
||||||
uint8_t *ptr_pixel = gb_pixels;
|
|
||||||
|
|
||||||
uint16_t dots_in_scanline = 0;
|
uint16_t dots_in_scanline = 0;
|
||||||
uint8_t line_buffer[160];
|
uint8_t line_buffer[160];
|
||||||
|
|
||||||
|
uint8_t *_LCDC = nullptr;
|
||||||
|
uint8_t *_STAT = nullptr;
|
||||||
|
uint8_t *_SCY = nullptr;
|
||||||
|
uint8_t *_SCX = nullptr;
|
||||||
|
uint8_t *_LY = nullptr;
|
||||||
|
uint8_t *_LYC = nullptr;
|
||||||
|
uint8_t *_BGP = nullptr;
|
||||||
|
uint8_t *_WY = nullptr;
|
||||||
|
uint8_t *_WX = nullptr;
|
||||||
|
uint8_t *OBP = nullptr;
|
||||||
|
|
||||||
|
oam_entry_t *oam = nullptr;
|
||||||
|
uint8_t *vram = nullptr;
|
||||||
|
|
||||||
|
#define LCDC (*_LCDC)
|
||||||
|
#define STAT (*_STAT)
|
||||||
|
#define SCY (*_SCY)
|
||||||
|
#define SCX (*_SCX)
|
||||||
|
#define LY (*_LY)
|
||||||
|
#define LYC (*_LYC)
|
||||||
|
#define BGP (*_BGP)
|
||||||
|
#define WY (*_WY)
|
||||||
|
#define WX (*_WX)
|
||||||
|
|
||||||
|
|
||||||
bool eventHandler(SDL_Event *e)
|
bool eventHandler(SDL_Event *e)
|
||||||
{
|
{
|
||||||
if (e->type==SDL_WINDOWEVENT) {
|
if (e->type==SDL_WINDOWEVENT) {
|
||||||
@@ -117,6 +146,20 @@ namespace gbscreen
|
|||||||
dest_rect.h = 144 * zoom;
|
dest_rect.h = 144 * zoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_LCDC = mem::rawHram(0xff40);
|
||||||
|
_STAT = mem::rawHram(0xff41);
|
||||||
|
_SCY = mem::rawHram(0xff42);
|
||||||
|
_SCX = mem::rawHram(0xff43);
|
||||||
|
_LY = mem::rawHram(0xff44);
|
||||||
|
_LYC = mem::rawHram(0xff45);
|
||||||
|
_BGP = mem::rawHram(0xff47);
|
||||||
|
_WY = mem::rawHram(0xff4a);
|
||||||
|
_WX = mem::rawHram(0xff4b);
|
||||||
|
OBP = mem::rawHram(0xff48);
|
||||||
|
|
||||||
|
oam = (oam_entry_t*)mem::rawHram(0xfe00);
|
||||||
|
vram = mem::rawVram();
|
||||||
|
|
||||||
focus();
|
focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,34 +176,30 @@ namespace gbscreen
|
|||||||
redraw();
|
redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fill_line_buffer_bkg(uint8_t LY)
|
void fill_line_buffer_bkg()
|
||||||
{
|
{
|
||||||
const uint8_t LCDC = mem::readMem(0xff40);
|
|
||||||
if ((LCDC & 0x1) == 0) {
|
if ((LCDC & 0x1) == 0) {
|
||||||
for (int i=0; i<160; ++i) line_buffer[i]=0;
|
for (int i=0; i<160; ++i) line_buffer[i]=0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const uint8_t SCY = mem::readMem(0xff42);
|
|
||||||
const uint8_t SCX = mem::readMem(0xff43);
|
|
||||||
const uint8_t BGP = mem::readMem(0xff47);
|
|
||||||
const uint16_t ty = uint8_t(SCY+LY) >> 3;
|
const uint16_t ty = uint8_t(SCY+LY) >> 3;
|
||||||
const uint8_t ly = uint8_t(SCY+LY) & 0x7;
|
const uint8_t ly = uint8_t(SCY+LY) & 0x7;
|
||||||
uint16_t tx = SCX >> 3;
|
uint16_t tx = SCX >> 3;
|
||||||
uint8_t ox = SCX & 0x7;
|
uint8_t ox = SCX & 0x7;
|
||||||
|
|
||||||
uint16_t base_tilemap_address = LCDC&0x8 ? 0x9c00 : 0x9800;
|
uint16_t base_tilemap_address = LCDC&0x8 ? 0x1c00 : 0x1800;
|
||||||
|
|
||||||
int pi = 0;
|
int pi = 0;
|
||||||
while(true) {
|
while(true) {
|
||||||
uint16_t tilemap_address = base_tilemap_address + tx + (ty<<5);
|
uint16_t tilemap_address = base_tilemap_address + tx + (ty<<5);
|
||||||
uint16_t tile = mem::readMem(tilemap_address);
|
uint16_t tile = vram[tilemap_address];
|
||||||
uint16_t base_tile_address = 0x8000;
|
uint16_t base_tile_address = 0x0000;
|
||||||
if ( ((LCDC&0x10)==0) && (tile<128) ) base_tile_address = 0x9000;
|
if ( ((LCDC&0x10)==0) && (tile<128) ) base_tile_address = 0x1000;
|
||||||
uint16_t tile_address = base_tile_address + (tile<<4) + (ly*2);
|
uint16_t tile_address = base_tile_address + (tile<<4) + (ly*2);
|
||||||
|
|
||||||
uint8_t a = mem::readMem(tile_address);
|
uint8_t a = vram[tile_address];
|
||||||
uint8_t b = mem::readMem(tile_address+1);
|
uint8_t b = vram[tile_address+1];
|
||||||
for (int i=0; i<8; ++i) {
|
for (int i=0; i<8; ++i) {
|
||||||
if (ox==0) {
|
if (ox==0) {
|
||||||
uint8_t index = (a&0x80 ? 1 : 0) + (b&0x80 ? 2 : 0 );
|
uint8_t index = (a&0x80 ? 1 : 0) + (b&0x80 ? 2 : 0 );
|
||||||
@@ -175,31 +214,27 @@ namespace gbscreen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fill_line_buffer_win(uint8_t LY)
|
void fill_line_buffer_win()
|
||||||
{
|
{
|
||||||
const uint8_t LCDC = mem::readMem(0xff40);
|
|
||||||
if ((LCDC & 0x21) != 0x21) return;
|
if ((LCDC & 0x21) != 0x21) return;
|
||||||
const uint8_t WY = mem::readMem(0xff4a);
|
|
||||||
if (LY<WY) return;
|
if (LY<WY) return;
|
||||||
const uint8_t WX = mem::readMem(0xff4b);
|
|
||||||
const uint8_t BGP = mem::readMem(0xff47);
|
|
||||||
const uint16_t ty = uint8_t(LY-WY) >> 3;
|
const uint16_t ty = uint8_t(LY-WY) >> 3;
|
||||||
const uint8_t ly = uint8_t(LY-WY) & 0x7;
|
const uint8_t ly = uint8_t(LY-WY) & 0x7;
|
||||||
uint8_t ox = WX<7 ? 7-WX : 0;
|
uint8_t ox = WX<7 ? 7-WX : 0;
|
||||||
uint16_t tx = 0;
|
uint16_t tx = 0;
|
||||||
|
|
||||||
uint16_t base_tilemap_address = LCDC&0x40 ? 0x9c00 : 0x9800;
|
uint16_t base_tilemap_address = LCDC&0x40 ? 0x1c00 : 0x1800;
|
||||||
|
|
||||||
int pi = WX<7 ? 0 : WX-7;
|
int pi = WX<7 ? 0 : WX-7;
|
||||||
while(true) {
|
while(true) {
|
||||||
uint16_t tilemap_address = base_tilemap_address + tx + (ty<<5);
|
uint16_t tilemap_address = base_tilemap_address + tx + (ty<<5);
|
||||||
uint16_t tile = mem::readMem(tilemap_address);
|
uint16_t tile = vram[tilemap_address];
|
||||||
uint16_t base_tile_address = 0x8000;
|
uint16_t base_tile_address = 0x0000;
|
||||||
if ( ((LCDC&0x10)==0) && (tile<128) ) base_tile_address = 0x9000;
|
if ( ((LCDC&0x10)==0) && (tile<128) ) base_tile_address = 0x1000;
|
||||||
uint16_t tile_address = base_tile_address + (tile<<4) + (ly*2);
|
uint16_t tile_address = base_tile_address + (tile<<4) + (ly*2);
|
||||||
|
|
||||||
uint8_t a = mem::readMem(tile_address);
|
uint8_t a = vram[tile_address];
|
||||||
uint8_t b = mem::readMem(tile_address+1);
|
uint8_t b = vram[tile_address+1];
|
||||||
for (int i=0; i<8; ++i) {
|
for (int i=0; i<8; ++i) {
|
||||||
if (ox==0) {
|
if (ox==0) {
|
||||||
uint8_t index = (a&0x80 ? 1 : 0) + (b&0x80 ? 2 : 0 );
|
uint8_t index = (a&0x80 ? 1 : 0) + (b&0x80 ? 2 : 0 );
|
||||||
@@ -214,19 +249,10 @@ namespace gbscreen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct oam_entry_t
|
void fill_line_buffer_obj()
|
||||||
{
|
{
|
||||||
uint8_t y, x, tile, attr;
|
|
||||||
};
|
|
||||||
oam_entry_t *oam = nullptr;
|
|
||||||
|
|
||||||
void fill_line_buffer_obj(uint8_t LY)
|
|
||||||
{
|
|
||||||
const uint8_t LCDC = mem::readMem(0xff40);
|
|
||||||
const uint8_t OBP[2] = { mem::readMem(0xff48), mem::readMem(0xff49) };
|
|
||||||
if ((LCDC & 0x2) == 0) return;
|
if ((LCDC & 0x2) == 0) return;
|
||||||
|
|
||||||
oam = (oam_entry_t*)mem::rawPtr(0xfe00);
|
|
||||||
const uint8_t height = (LCDC & 0x4) ? 16 : 8;
|
const uint8_t height = (LCDC & 0x4) ? 16 : 8;
|
||||||
uint8_t obj_list[10] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
|
uint8_t obj_list[10] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
|
||||||
int num_obj_found=0;
|
int num_obj_found=0;
|
||||||
@@ -251,10 +277,10 @@ namespace gbscreen
|
|||||||
|
|
||||||
uint16_t tile = height==8 ? o->tile : o->tile & 0xFE; // si es dos tiles de alt, el primer sempre comença en numero parell
|
uint16_t tile = height==8 ? o->tile : o->tile & 0xFE; // si es dos tiles de alt, el primer sempre comença en numero parell
|
||||||
uint8_t yflip = o->attr&0x40 ? (height-1)-ly : ly; // està invertit verticalment?
|
uint8_t yflip = o->attr&0x40 ? (height-1)-ly : ly; // està invertit verticalment?
|
||||||
uint16_t tile_address = 0x8000 + (tile<<4) + (yflip*2);
|
uint16_t tile_address = 0x0000 + (tile<<4) + (yflip*2);
|
||||||
|
|
||||||
uint8_t a = mem::readMem(tile_address);
|
uint8_t a = vram[tile_address];
|
||||||
uint8_t b = mem::readMem(tile_address+1);
|
uint8_t b = vram[tile_address+1];
|
||||||
|
|
||||||
for (int i=0; i<8; ++i) { // Per a cada pixel de la linea del tile...
|
for (int i=0; i<8; ++i) { // Per a cada pixel de la linea del tile...
|
||||||
if (o->x+i>=168) break; // Si ja estem fora de la pantalla per la dreta, eixim del bucle
|
if (o->x+i>=168) break; // Si ja estem fora de la pantalla per la dreta, eixim del bucle
|
||||||
@@ -285,19 +311,14 @@ namespace gbscreen
|
|||||||
|
|
||||||
void refresh(const uint32_t dt, const bool full)
|
void refresh(const uint32_t dt, const bool full)
|
||||||
{
|
{
|
||||||
const uint8_t LCDC = mem::readMem(0xff40);
|
|
||||||
if ((LCDC&0x80)==0) return;
|
if ((LCDC&0x80)==0) return;
|
||||||
|
|
||||||
uint8_t STAT = mem::readMem(0xff41);
|
|
||||||
uint8_t LY = mem::readMem(0xff44);
|
|
||||||
const uint8_t LYC = mem::readMem(0xff45);
|
|
||||||
for (int i=0;i<dt;++i)
|
for (int i=0;i<dt;++i)
|
||||||
{
|
{
|
||||||
// Açò va volcant els pixels del line_buffer en pantalla
|
// Açò va volcant els pixels del line_buffer en pantalla
|
||||||
if ( (STAT&0x3)==3) {
|
if ( (STAT&0x3)==3) {
|
||||||
uint16_t current_pixel = dots_in_scanline-80;
|
uint16_t current_pixel = dots_in_scanline-80;
|
||||||
if (current_pixel<160) {
|
if (current_pixel<160) {
|
||||||
//*(ptr_pixel++) = line_buffer[current_pixel];
|
|
||||||
gb_pixels[current_pixel+LY*160] = line_buffer[current_pixel];
|
gb_pixels[current_pixel+LY*160] = line_buffer[current_pixel];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,11 +339,10 @@ namespace gbscreen
|
|||||||
{
|
{
|
||||||
dots_in_scanline = 0;
|
dots_in_scanline = 0;
|
||||||
LY++;
|
LY++;
|
||||||
|
if (LY==154) LY=0;
|
||||||
if (LY==144)
|
if (LY==144)
|
||||||
{
|
{
|
||||||
STAT = (STAT & 0xFC) | 0x01; // Set mode 1
|
STAT = (STAT & 0xFC) | 0x01; // Set mode 1
|
||||||
//mem::writeMem(0xff41, STAT);
|
|
||||||
//mem::writeMem(0xff44, LY);
|
|
||||||
interrupts |= INTERRUPT_VBLANK;
|
interrupts |= INTERRUPT_VBLANK;
|
||||||
if (STAT&0x10) interrupts |= INTERRUPT_LCD;
|
if (STAT&0x10) interrupts |= INTERRUPT_LCD;
|
||||||
}
|
}
|
||||||
@@ -332,13 +352,9 @@ namespace gbscreen
|
|||||||
{
|
{
|
||||||
STAT = (STAT & 0xFC) | 0x02; // Set mode 2
|
STAT = (STAT & 0xFC) | 0x02; // Set mode 2
|
||||||
if (STAT&0x20) interrupts |= INTERRUPT_LCD;
|
if (STAT&0x20) interrupts |= INTERRUPT_LCD;
|
||||||
fill_line_buffer_bkg(LY);
|
fill_line_buffer_bkg();
|
||||||
fill_line_buffer_win(LY);
|
fill_line_buffer_win();
|
||||||
fill_line_buffer_obj(LY);
|
fill_line_buffer_obj();
|
||||||
}
|
|
||||||
else if (LY==154)
|
|
||||||
{
|
|
||||||
LY=0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (LY==LYC)
|
if (LY==LYC)
|
||||||
@@ -354,32 +370,23 @@ namespace gbscreen
|
|||||||
|
|
||||||
if (interrupts)
|
if (interrupts)
|
||||||
{
|
{
|
||||||
mem::writeMem(0xff41, STAT);
|
|
||||||
mem::writeMem(0xff44, LY);
|
|
||||||
sm83::interrupt(interrupts);
|
sm83::interrupt(interrupts);
|
||||||
}
|
}
|
||||||
|
|
||||||
t_screen++;
|
t_screen++;
|
||||||
if (t_screen>=t_states_total)
|
if (t_screen>=t_states_total)
|
||||||
{
|
{
|
||||||
t_screen=0;
|
t_screen-=t_states_total;
|
||||||
ptr_pixel = gb_pixels;
|
|
||||||
redraw();
|
redraw();
|
||||||
//if (!full) sm83::interrupt(INTERRUPT_VBLANK);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mem::writeMem(0xff41, STAT);
|
|
||||||
mem::writeMem(0xff44, LY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fullrefresh()
|
void fullrefresh()
|
||||||
{
|
{
|
||||||
uint32_t tmp = t_screen;
|
uint32_t tmp = t_screen;
|
||||||
t_screen = 0;
|
t_screen = 0;
|
||||||
//uint8_t * tmp_ptr = ptr_pixel;
|
|
||||||
//ptr_pixel = gb_pixels;
|
|
||||||
refresh(t_states_total, true);
|
refresh(t_states_total, true);
|
||||||
//ptr_pixel = tmp_ptr;
|
|
||||||
t_screen = tmp;
|
t_screen = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,7 +427,7 @@ namespace gbscreen
|
|||||||
if (fullscreen) SDL_GetWindowSize(win, &rect.w, &rect.h);
|
if (fullscreen) SDL_GetWindowSize(win, &rect.w, &rect.h);
|
||||||
SDL_RenderFillRect(ren, &rect);
|
SDL_RenderFillRect(ren, &rect);
|
||||||
}
|
}
|
||||||
audio_viewer::refresh();
|
//audio_viewer::refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void present()
|
void present()
|
||||||
|
|||||||
17
main.cpp
17
main.cpp
@@ -122,13 +122,13 @@ int main(int argc, char *argv[])
|
|||||||
bool result = true;
|
bool result = true;
|
||||||
|
|
||||||
if (e.type == SDL_QUIT) { should_exit=true; break; }
|
if (e.type == SDL_QUIT) { should_exit=true; break; }
|
||||||
if (e.type == SDL_MOUSEBUTTONDOWN) result = ui::window::sendEvent(e.button.windowID, &e);
|
else if (e.type == SDL_MOUSEBUTTONDOWN) result = ui::window::sendEvent(e.button.windowID, &e);
|
||||||
if (e.type == SDL_MOUSEBUTTONUP) result = ui::window::sendEvent(e.button.windowID, &e);
|
else if (e.type == SDL_MOUSEBUTTONUP) result = ui::window::sendEvent(e.button.windowID, &e);
|
||||||
if (e.type == SDL_MOUSEMOTION) result = ui::window::sendEvent(e.motion.windowID, &e);
|
else if (e.type == SDL_MOUSEMOTION) result = ui::window::sendEvent(e.motion.windowID, &e);
|
||||||
if (e.type == SDL_WINDOWEVENT) result = ui::window::sendEvent(e.window.windowID, &e);
|
else if (e.type == SDL_WINDOWEVENT) result = ui::window::sendEvent(e.window.windowID, &e);
|
||||||
if (e.type == SDL_MOUSEWHEEL) result = ui::window::sendEvent(e.wheel.windowID, &e);
|
else if (e.type == SDL_MOUSEWHEEL) result = ui::window::sendEvent(e.wheel.windowID, &e);
|
||||||
if (e.type == SDL_TEXTINPUT) result = ui::window::sendEvent(e.text.windowID, &e);
|
else if (e.type == SDL_TEXTINPUT) result = ui::window::sendEvent(e.text.windowID, &e);
|
||||||
if (e.type == SDL_KEYDOWN) {
|
else if (e.type == SDL_KEYDOWN) {
|
||||||
if (e.key.keysym.scancode==SDL_SCANCODE_F5) {
|
if (e.key.keysym.scancode==SDL_SCANCODE_F5) {
|
||||||
if (debug::debugging()) {
|
if (debug::debugging()) {
|
||||||
debug::history::gototop();
|
debug::history::gototop();
|
||||||
@@ -177,8 +177,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
result = ui::window::sendEvent(e.key.windowID, &e);
|
result = ui::window::sendEvent(e.key.windowID, &e);
|
||||||
}
|
}
|
||||||
|
else if (e.type == SDL_MOUSEBUTTONUP && e.button.button==1) ui::setClicked(true);
|
||||||
if (e.type == SDL_MOUSEBUTTONUP && e.button.button==1) ui::setClicked(true);
|
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
should_exit = true; break;
|
should_exit = true; break;
|
||||||
|
|||||||
7
mem.cpp
7
mem.cpp
@@ -186,7 +186,12 @@ namespace mem
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *rawPtr(uint16_t address)
|
uint8_t *rawVram()
|
||||||
|
{
|
||||||
|
return vram;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *rawHram(uint16_t address)
|
||||||
{
|
{
|
||||||
return &hram[address-0xfe00];
|
return &hram[address-0xfe00];
|
||||||
}
|
}
|
||||||
|
|||||||
3
mem.h
3
mem.h
@@ -36,7 +36,8 @@ namespace mem
|
|||||||
void saveState(FILE* f);
|
void saveState(FILE* f);
|
||||||
void loadState(FILE* f);
|
void loadState(FILE* f);
|
||||||
|
|
||||||
uint8_t *rawPtr(uint16_t address);
|
uint8_t *rawVram();
|
||||||
|
uint8_t *rawHram(uint16_t address);
|
||||||
|
|
||||||
void init_dma_transfer(uint8_t source);
|
void init_dma_transfer(uint8_t source);
|
||||||
void update_mapped(const uint32_t dt);
|
void update_mapped(const uint32_t dt);
|
||||||
|
|||||||
4
sm83.cpp
4
sm83.cpp
@@ -522,8 +522,8 @@ namespace sm83
|
|||||||
|
|
||||||
void processInterrupts()
|
void processInterrupts()
|
||||||
{
|
{
|
||||||
uint8_t *IE = mem::rawPtr(0xffff);
|
uint8_t *IE = mem::rawHram(0xffff);
|
||||||
uint8_t *IF = mem::rawPtr(0xff0f);
|
uint8_t *IF = mem::rawHram(0xff0f);
|
||||||
if ( (*IF & *IE) == 0 ) return;
|
if ( (*IF & *IE) == 0 ) return;
|
||||||
if (halted) {
|
if (halted) {
|
||||||
//exit_from_halt = true;
|
//exit_from_halt = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user