mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-11-10 21:05:12 +01:00
Merge pull request #184 from bkoropoff/overclock-tidy
[Core] Improved CPU overclock granularity and added (optional) sprite limits removal support (bkoropoff)
This commit is contained in:
commit
5ec1cd099f
@ -440,8 +440,8 @@ LIBRETRO_CFLAGS += $(BPP_DEFINES) \
|
||||
$(ENDIANNESS_DEFINES) \
|
||||
$(PLATFORM_DEFINES) \
|
||||
-D__LIBRETRO__ \
|
||||
-DM68K_ALLOW_OVERCLOCK \
|
||||
-DZ80_ALLOW_OVERCLOCK
|
||||
-DM68K_OVERCLOCK_SHIFT=20 \
|
||||
-DZ80_OVERCLOCK_SHIFT=20
|
||||
|
||||
ifneq (,$(findstring msvc,$(platform)))
|
||||
LIBRETRO_CFLAGS += -DINLINE="static _inline"
|
||||
|
@ -268,8 +268,8 @@ typedef struct
|
||||
|
||||
uint address_space; /* Current FC code */
|
||||
|
||||
#ifdef M68K_ALLOW_OVERCLOCK
|
||||
uint8 overclock_ratio;
|
||||
#ifdef M68K_OVERCLOCK_SHIFT
|
||||
int cycle_ratio;
|
||||
#endif
|
||||
|
||||
/* Callbacks to host */
|
||||
|
@ -319,8 +319,8 @@ void m68k_init(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef M68K_ALLOW_OVERCLOCK
|
||||
m68k.overclock_ratio = 1;
|
||||
#ifdef M68K_OVERCLOCK_SHIFT
|
||||
m68k.cycle_ratio = 1 << M68K_OVERCLOCK_SHIFT;
|
||||
#endif
|
||||
|
||||
#if M68K_EMULATE_INT_ACK == OPT_ON
|
||||
|
@ -514,8 +514,8 @@
|
||||
|
||||
/* ---------------------------- Cycle Counting ---------------------------- */
|
||||
|
||||
#ifdef M68K_ALLOW_OVERCLOCK
|
||||
#define USE_CYCLES(A) m68ki_cpu.cycles += (A) / m68ki_cpu.overclock_ratio
|
||||
#ifdef M68K_OVERCLOCK_SHIFT
|
||||
#define USE_CYCLES(A) m68ki_cpu.cycles += ((A) * m68ki_cpu.cycle_ratio) >> M68K_OVERCLOCK_SHIFT
|
||||
#else
|
||||
#define USE_CYCLES(A) m68ki_cpu.cycles += (A)
|
||||
#endif
|
||||
|
@ -284,8 +284,8 @@ void s68k_init(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef M68K_ALLOW_OVERCLOCK
|
||||
s68k.overclock_ratio = 1;
|
||||
#ifdef M68K_OVERCLOCK_SHIFT
|
||||
s68k.cycle_ratio = 1 << M68K_OVERCLOCK_SHIFT;
|
||||
#endif
|
||||
|
||||
#if M68K_EMULATE_INT_ACK == OPT_ON
|
||||
|
@ -43,6 +43,14 @@
|
||||
#include "md_ntsc.h"
|
||||
#include "sms_ntsc.h"
|
||||
|
||||
#ifndef HAVE_NO_SPRITE_LIMIT
|
||||
#define MAX_SPRITES_PER_LINE 20
|
||||
#define TMS_MAX_SPRITES_PER_LINE 4
|
||||
#define MODE4_MAX_SPRITES_PER_LINE 8
|
||||
#define MODE5_MAX_SPRITES_PER_LINE (bitmap.viewport.w >> 4)
|
||||
#define MODE5_MAX_SPRITE_PIXELS max_sprite_pixels
|
||||
#endif
|
||||
|
||||
/*** NTSC Filters ***/
|
||||
extern md_ntsc_t *md_ntsc;
|
||||
extern sms_ntsc_t *sms_ntsc;
|
||||
@ -584,7 +592,7 @@ typedef struct
|
||||
uint16 size;
|
||||
} object_info_t;
|
||||
|
||||
static object_info_t obj_info[2][20];
|
||||
static object_info_t obj_info[2][MAX_SPRITES_PER_LINE];
|
||||
|
||||
/* Sprite Counter */
|
||||
static uint8 object_count[2];
|
||||
@ -3149,6 +3157,7 @@ void render_obj_m5(int line)
|
||||
int xpos, width;
|
||||
int pixelcount = 0;
|
||||
int masked = 0;
|
||||
int max_pixels = MODE5_MAX_SPRITE_PIXELS;
|
||||
|
||||
uint8 *src, *s, *lb;
|
||||
uint32 temp, v_line;
|
||||
@ -3213,10 +3222,10 @@ void render_obj_m5(int line)
|
||||
lb = &linebuf[0][0x20 + xpos];
|
||||
|
||||
/* Max. number of sprite pixels rendered per line */
|
||||
if (pixelcount > max_sprite_pixels)
|
||||
if (pixelcount > max_pixels)
|
||||
{
|
||||
/* Adjust number of pixels to draw */
|
||||
width -= (pixelcount - max_sprite_pixels);
|
||||
width -= (pixelcount - max_pixels);
|
||||
}
|
||||
|
||||
/* Number of tiles to draw */
|
||||
@ -3235,7 +3244,7 @@ void render_obj_m5(int line)
|
||||
}
|
||||
|
||||
/* Sprite limit */
|
||||
if (pixelcount >= max_sprite_pixels)
|
||||
if (pixelcount >= max_pixels)
|
||||
{
|
||||
/* Sprite masking is effective on next line if max pixel width is reached */
|
||||
spr_ovr = (pixelcount >= bitmap.viewport.w);
|
||||
@ -3258,6 +3267,7 @@ void render_obj_m5_ste(int line)
|
||||
int xpos, width;
|
||||
int pixelcount = 0;
|
||||
int masked = 0;
|
||||
int max_pixels = MODE5_MAX_SPRITE_PIXELS;
|
||||
|
||||
uint8 *src, *s, *lb;
|
||||
uint32 temp, v_line;
|
||||
@ -3325,9 +3335,9 @@ void render_obj_m5_ste(int line)
|
||||
lb = &linebuf[1][0x20 + xpos];
|
||||
|
||||
/* Adjust number of pixels to draw for sprite limit */
|
||||
if (pixelcount > max_sprite_pixels)
|
||||
if (pixelcount > max_pixels)
|
||||
{
|
||||
width -= (pixelcount - max_sprite_pixels);
|
||||
width -= (pixelcount - max_pixels);
|
||||
}
|
||||
|
||||
/* Number of tiles to draw */
|
||||
@ -3346,7 +3356,7 @@ void render_obj_m5_ste(int line)
|
||||
}
|
||||
|
||||
/* Sprite limit */
|
||||
if (pixelcount >= max_sprite_pixels)
|
||||
if (pixelcount >= max_pixels)
|
||||
{
|
||||
/* Sprite masking is effective on next line if max pixel width is reached */
|
||||
spr_ovr = (pixelcount >= bitmap.viewport.w);
|
||||
@ -3376,6 +3386,7 @@ void render_obj_m5_im2(int line)
|
||||
int pixelcount = 0;
|
||||
int masked = 0;
|
||||
int odd = odd_frame;
|
||||
int max_pixels = MODE5_MAX_SPRITE_PIXELS;
|
||||
|
||||
uint8 *src, *s, *lb;
|
||||
uint32 temp, v_line;
|
||||
@ -3440,9 +3451,9 @@ void render_obj_m5_im2(int line)
|
||||
lb = &linebuf[0][0x20 + xpos];
|
||||
|
||||
/* Adjust width for sprite limit */
|
||||
if (pixelcount > max_sprite_pixels)
|
||||
if (pixelcount > max_pixels)
|
||||
{
|
||||
width -= (pixelcount - max_sprite_pixels);
|
||||
width -= (pixelcount - max_pixels);
|
||||
}
|
||||
|
||||
/* Number of tiles to draw */
|
||||
@ -3461,7 +3472,7 @@ void render_obj_m5_im2(int line)
|
||||
}
|
||||
|
||||
/* Sprite Limit */
|
||||
if (pixelcount >= max_sprite_pixels)
|
||||
if (pixelcount >= max_pixels)
|
||||
{
|
||||
/* Sprite masking is effective on next line if max pixel width is reached */
|
||||
spr_ovr = (pixelcount >= bitmap.viewport.w);
|
||||
@ -3485,6 +3496,7 @@ void render_obj_m5_im2_ste(int line)
|
||||
int pixelcount = 0;
|
||||
int masked = 0;
|
||||
int odd = odd_frame;
|
||||
int max_pixels = MODE5_MAX_SPRITE_PIXELS;
|
||||
|
||||
uint8 *src, *s, *lb;
|
||||
uint32 temp, v_line;
|
||||
@ -3552,9 +3564,9 @@ void render_obj_m5_im2_ste(int line)
|
||||
lb = &linebuf[1][0x20 + xpos];
|
||||
|
||||
/* Adjust width for sprite limit */
|
||||
if (pixelcount > max_sprite_pixels)
|
||||
if (pixelcount > max_pixels)
|
||||
{
|
||||
width -= (pixelcount - max_sprite_pixels);
|
||||
width -= (pixelcount - max_pixels);
|
||||
}
|
||||
|
||||
/* Number of tiles to draw */
|
||||
@ -3573,7 +3585,7 @@ void render_obj_m5_im2_ste(int line)
|
||||
}
|
||||
|
||||
/* Sprite Limit */
|
||||
if (pixelcount >= max_sprite_pixels)
|
||||
if (pixelcount >= max_pixels)
|
||||
{
|
||||
/* Sprite masking is effective on next line if max pixel width is reached */
|
||||
spr_ovr = (pixelcount >= bitmap.viewport.w);
|
||||
@ -3654,7 +3666,7 @@ void parse_satb_tms(int line)
|
||||
if ((ypos >= 0) && (ypos < height))
|
||||
{
|
||||
/* Sprite overflow */
|
||||
if (count == 4)
|
||||
if (count == TMS_MAX_SPRITES_PER_LINE)
|
||||
{
|
||||
/* Flag is set only during active area */
|
||||
if (line < bitmap.viewport.h)
|
||||
@ -3750,7 +3762,7 @@ void parse_satb_m4(int line)
|
||||
if ((ypos >= 0) && (ypos < height))
|
||||
{
|
||||
/* Sprite overflow */
|
||||
if (count == 8)
|
||||
if (count == MODE4_MAX_SPRITES_PER_LINE)
|
||||
{
|
||||
/* Flag is set only during active area */
|
||||
if ((line >= 0) && (line < bitmap.viewport.h))
|
||||
@ -3796,7 +3808,7 @@ void parse_satb_m5(int line)
|
||||
int count = 0;
|
||||
|
||||
/* max. number of rendered sprites (16 or 20 sprites per line by default) */
|
||||
int max = bitmap.viewport.w >> 4;
|
||||
int max = MODE5_MAX_SPRITES_PER_LINE;
|
||||
|
||||
/* max. number of parsed sprites (64 or 80 sprites per line by default) */
|
||||
int total = max_sprite_pixels >> 2;
|
||||
|
@ -201,18 +201,15 @@
|
||||
#define IFF2 Z80.iff2
|
||||
#define HALT Z80.halt
|
||||
|
||||
#ifdef Z80_ALLOW_OVERCLOCK
|
||||
#define USE_CYCLES(A) Z80.cycles += (A) / z80_overclock_ratio
|
||||
#ifdef Z80_OVERCLOCK_SHIFT
|
||||
#define USE_CYCLES(A) Z80.cycles += ((A) * z80_cycle_ratio) >> Z80_OVERCLOCK_SHIFT
|
||||
UINT32 z80_cycle_ratio;
|
||||
#else
|
||||
#define USE_CYCLES(A) Z80.cycles += (A)
|
||||
#endif
|
||||
|
||||
Z80_Regs Z80;
|
||||
|
||||
#ifdef Z80_ALLOW_OVERCLOCK
|
||||
UINT8 z80_overclock_ratio;
|
||||
#endif
|
||||
|
||||
unsigned char *z80_readmap[64];
|
||||
unsigned char *z80_writemap[64];
|
||||
|
||||
@ -3368,8 +3365,8 @@ void z80_init(const void *config, int (*irqcallback)(int))
|
||||
memset(&Z80, 0, sizeof(Z80));
|
||||
Z80.daisy = config;
|
||||
Z80.irq_callback = irqcallback;
|
||||
#ifdef Z80_ALLOW_OVERCLOCK
|
||||
z80_overclock_ratio = 1;
|
||||
#ifdef Z80_OVERCLOCK_SHIFT
|
||||
z80_cycle_ratio = 1 << Z80_OVERCLOCK_SHIFT;
|
||||
#endif
|
||||
|
||||
/* Clear registers values (NB: should be random on real hardware ?) */
|
||||
|
@ -51,8 +51,8 @@ typedef struct
|
||||
|
||||
extern Z80_Regs Z80;
|
||||
|
||||
#ifdef Z80_ALLOW_OVERCLOCK
|
||||
extern UINT8 z80_overclock_ratio;
|
||||
#ifdef Z80_OVERCLOCK_SHIFT
|
||||
extern UINT32 z80_cycle_ratio;
|
||||
#endif
|
||||
|
||||
extern unsigned char *z80_readmap[64];
|
||||
|
@ -54,12 +54,6 @@
|
||||
#include <xtl.h>
|
||||
#endif
|
||||
|
||||
#if defined(M68K_ALLOW_OVERCLOCK) || defined(Z80_ALLOW_OVERCLOCK)
|
||||
#define HAVE_OVERCLOCK
|
||||
/* Overclocking frame delay (hack) */
|
||||
#define OVERCLOCK_FRAME_DELAY 100
|
||||
#endif
|
||||
|
||||
#define RETRO_DEVICE_MDPAD_3B RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 0)
|
||||
#define RETRO_DEVICE_MDPAD_6B RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 1)
|
||||
#define RETRO_DEVICE_MSPAD_2B RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 2)
|
||||
@ -82,6 +76,25 @@
|
||||
#include "sms_ntsc.h"
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
#define STATIC_ASSERT(name, test) typedef struct { int assert_[(test)?1:-1]; } assert_ ## name ## _
|
||||
#define M68K_MAX_CYCLES 1107
|
||||
#define Z80_MAX_CYCLES 345
|
||||
#define OVERCLOCK_FRAME_DELAY 100
|
||||
|
||||
#ifdef M68K_OVERCLOCK_SHIFT
|
||||
#define HAVE_OVERCLOCK
|
||||
STATIC_ASSERT(m68k_overflow,
|
||||
M68K_MAX_CYCLES <= UINT_MAX >> (M68K_OVERCLOCK_SHIFT + 1));
|
||||
#endif
|
||||
|
||||
#ifdef Z80_OVERCLOCK_SHIFT
|
||||
#ifndef HAVE_OVERCLOCK
|
||||
#define HAVE_OVERCLOCK
|
||||
#endif
|
||||
STATIC_ASSERT(z80_overflow,
|
||||
Z80_MAX_CYCLES <= UINT_MAX >> (Z80_OVERCLOCK_SHIFT + 1));
|
||||
#endif
|
||||
|
||||
sms_ntsc_t *sms_ntsc;
|
||||
md_ntsc_t *md_ntsc;
|
||||
|
||||
@ -541,8 +554,9 @@ static void config_default(void)
|
||||
config.lock_on = 0;
|
||||
config.lcd = 0; /* 0.8 fixed point */
|
||||
#ifdef HAVE_OVERCLOCK
|
||||
config.overclock = 0;
|
||||
config.overclock = 100;
|
||||
#endif
|
||||
config.no_sprite_limit = 0;
|
||||
|
||||
/* video options */
|
||||
config.overscan = 0;
|
||||
@ -811,6 +825,31 @@ static bool update_viewport(void)
|
||||
return ((ow != vwidth) || (oh != vheight) || (oar != vaspect_ratio));
|
||||
}
|
||||
|
||||
#ifdef HAVE_OVERCLOCK
|
||||
static void update_overclock(void)
|
||||
{
|
||||
#ifdef M68K_OVERCLOCK_SHIFT
|
||||
m68k.cycle_ratio = 1 << M68K_OVERCLOCK_SHIFT;
|
||||
#endif
|
||||
#ifdef Z80_OVERCLOCK_SHIFT
|
||||
z80_cycle_ratio = 1 << Z80_OVERCLOCK_SHIFT;
|
||||
#endif
|
||||
if (overclock_delay == 0)
|
||||
{
|
||||
/* Cycle ratios multiply per-instruction cycle counts, so use
|
||||
reciprocals */
|
||||
#ifdef M68K_OVERCLOCK_SHIFT
|
||||
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
|
||||
m68k.cycle_ratio = (100 << M68K_OVERCLOCK_SHIFT) / config.overclock;
|
||||
#endif
|
||||
#ifdef Z80_OVERCLOCK_SHIFT
|
||||
if ((system_hw & SYSTEM_PBC) != SYSTEM_MD)
|
||||
z80_cycle_ratio = (100 << Z80_OVERCLOCK_SHIFT) / config.overclock;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void check_variables(void)
|
||||
{
|
||||
unsigned orig_value;
|
||||
@ -1270,13 +1309,31 @@ static void check_variables(void)
|
||||
var.key = "genesis_plus_gx_overclock";
|
||||
environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var);
|
||||
{
|
||||
if (strcmp(var.value, "1x") == 0)
|
||||
config.overclock = 0;
|
||||
else if (strcmp(var.value, "2x") == 0)
|
||||
config.overclock = 1;
|
||||
if (strcmp(var.value, "100%") == 0)
|
||||
config.overclock = 100;
|
||||
else if (strcmp(var.value, "125%") == 0)
|
||||
config.overclock = 125;
|
||||
else if (strcmp(var.value, "150%") == 0)
|
||||
config.overclock = 150;
|
||||
else if (strcmp(var.value, "175%") == 0)
|
||||
config.overclock = 175;
|
||||
else if (strcmp(var.value, "200%") == 0)
|
||||
config.overclock = 200;
|
||||
|
||||
if (system_hw)
|
||||
update_overclock();
|
||||
}
|
||||
#endif
|
||||
|
||||
var.key = "genesis_plus_gx_no_sprite_limit";
|
||||
environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var);
|
||||
{
|
||||
if (strcmp(var.value, "disabled") == 0)
|
||||
config.no_sprite_limit = 0;
|
||||
else
|
||||
config.no_sprite_limit = 1;
|
||||
}
|
||||
|
||||
if (reinit)
|
||||
{
|
||||
#ifdef HAVE_OVERCLOCK
|
||||
@ -1722,8 +1779,9 @@ void retro_set_environment(retro_environment_t cb)
|
||||
{ "genesis_plus_gx_gun_cursor", "Show Lightgun crosshair; disabled|enabled" },
|
||||
{ "genesis_plus_gx_invert_mouse", "Invert Mouse Y-axis; disabled|enabled" },
|
||||
#ifdef HAVE_OVERCLOCK
|
||||
{ "genesis_plus_gx_overclock", "Overclock CPU; 1x|2x" },
|
||||
{ "genesis_plus_gx_overclock", "CPU speed; 100%|125%|150%|175%|200%" },
|
||||
#endif
|
||||
{ "genesis_plus_gx_no_sprite_limit", "Remove per-line sprite limit; disabled|enabled" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
@ -2069,6 +2127,7 @@ bool retro_unserialize(const void *data, size_t size)
|
||||
|
||||
#ifdef HAVE_OVERCLOCK
|
||||
overclock_delay = OVERCLOCK_FRAME_DELAY;
|
||||
update_overclock();
|
||||
#endif
|
||||
|
||||
return TRUE;
|
||||
@ -2235,9 +2294,6 @@ bool retro_load_game(const struct retro_game_info *info)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_OVERCLOCK
|
||||
overclock_delay = OVERCLOCK_FRAME_DELAY;
|
||||
#endif
|
||||
audio_init(SOUND_FREQUENCY, 0);
|
||||
system_init();
|
||||
system_reset();
|
||||
@ -2248,6 +2304,11 @@ bool retro_load_game(const struct retro_game_info *info)
|
||||
|
||||
update_viewport();
|
||||
|
||||
#ifdef HAVE_OVERCLOCK
|
||||
overclock_delay = OVERCLOCK_FRAME_DELAY;
|
||||
update_overclock();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2355,6 +2416,7 @@ void retro_reset(void)
|
||||
{
|
||||
#ifdef HAVE_OVERCLOCK
|
||||
overclock_delay = OVERCLOCK_FRAME_DELAY;
|
||||
update_overclock();
|
||||
#endif
|
||||
gen_reset(0);
|
||||
}
|
||||
@ -2366,38 +2428,20 @@ void retro_run(void)
|
||||
|
||||
#ifdef HAVE_OVERCLOCK
|
||||
/* update overclock delay */
|
||||
if (overclock_delay)
|
||||
overclock_delay--;
|
||||
if (overclock_delay && --overclock_delay == 0)
|
||||
update_overclock();
|
||||
#endif
|
||||
|
||||
if (system_hw == SYSTEM_MCD)
|
||||
{
|
||||
#ifdef M68K_ALLOW_OVERCLOCK
|
||||
if (config.overclock && overclock_delay == 0)
|
||||
m68k.overclock_ratio = 2;
|
||||
else
|
||||
m68k.overclock_ratio = 1;
|
||||
#endif
|
||||
system_frame_scd(0);
|
||||
}
|
||||
else if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
|
||||
{
|
||||
#ifdef M68K_ALLOW_OVERCLOCK
|
||||
if (config.overclock && overclock_delay == 0)
|
||||
m68k.overclock_ratio = 2;
|
||||
else
|
||||
m68k.overclock_ratio = 1;
|
||||
#endif
|
||||
system_frame_gen(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef Z80_ALLOW_OVERCLOCK
|
||||
if (config.overclock && overclock_delay == 0)
|
||||
z80_overclock_ratio = 2;
|
||||
else
|
||||
z80_overclock_ratio = 1;
|
||||
#endif
|
||||
system_frame_sms(0);
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,14 @@ typedef unsigned char bool;
|
||||
|
||||
#define HAVE_YM3438_CORE
|
||||
|
||||
typedef struct
|
||||
#define HAVE_NO_SPRITE_LIMIT
|
||||
#define MAX_SPRITES_PER_LINE 80
|
||||
#define TMS_MAX_SPRITES_PER_LINE (config.no_sprite_limit ? MAX_SPRITES_PER_LINE : 4)
|
||||
#define MODE4_MAX_SPRITES_PER_LINE (config.no_sprite_limit ? MAX_SPRITES_PER_LINE : 8)
|
||||
#define MODE5_MAX_SPRITES_PER_LINE (config.no_sprite_limit ? MAX_SPRITES_PER_LINE : (bitmap.viewport.w >> 4))
|
||||
#define MODE5_MAX_SPRITE_PIXELS (config.no_sprite_limit ? MAX_SPRITES_PER_LINE * 32 : max_sprite_pixels)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int8 device;
|
||||
uint8 port;
|
||||
@ -120,7 +127,8 @@ struct
|
||||
t_input_config input[MAX_INPUTS];
|
||||
uint8 invert_mouse;
|
||||
uint8 gun_cursor;
|
||||
uint8 overclock;
|
||||
uint32 overclock;
|
||||
uint8 no_sprite_limit;
|
||||
} config;
|
||||
|
||||
extern char GG_ROM[256];
|
||||
|
Loading…
Reference in New Issue
Block a user