Merge pull request #505 from NRS-NewRisingSun/mappers
Updates to mappers 256, 289, 344, 432.
This commit is contained in:
@@ -32,18 +32,19 @@ static void M432CW(uint32 A, uint8 V) {
|
||||
static void M432PW(uint32 A, uint8 V) {
|
||||
int prgAND = (EXPREGS[1] & 0x02) ? 0x0F : 0x1F;
|
||||
int prgOR = ((EXPREGS[1] << 4) & 0x10) | (EXPREGS[1] << 1) & 0x20;
|
||||
if ((A < 0xC000) || (~EXPREGS[1] & 0x40)) setprg8(A, (V & prgAND) | (prgOR & ~prgAND));
|
||||
if ((A < 0xC000) && (EXPREGS[1] & 0x40)) setprg8(A | 0x4000, (V & prgAND) | (prgOR & ~prgAND));
|
||||
if ((A < 0xC000) || (~EXPREGS[1] & 0x40)) setprg8(A, (V & prgAND) | (prgOR & ~prgAND) & (EXPREGS[1] & 0x80?~2:~0));
|
||||
if ((A < 0xC000) && (EXPREGS[1] & 0x40)) setprg8(A | 0x4000, (V & prgAND) | (prgOR & ~prgAND) | (EXPREGS[1] & 0x80? 2: 0));
|
||||
}
|
||||
|
||||
static DECLFR(M432Read) {
|
||||
if (EXPREGS[0] & 1)
|
||||
if (EXPREGS[0] & 1 || EXPREGS[1] & 0x20)
|
||||
return EXPREGS[2];
|
||||
return CartBR(A);
|
||||
}
|
||||
|
||||
static DECLFW(M432Write) {
|
||||
EXPREGS[A & 1] = V;
|
||||
if (~A &1 && ~V &1) EXPREGS[1] =0; /* Writing 0 to register 0 clears register 1 */
|
||||
FixMMC3PRG(MMC3_cmd);
|
||||
FixMMC3CHR(MMC3_cmd);
|
||||
}
|
||||
@@ -51,7 +52,7 @@ static DECLFW(M432Write) {
|
||||
static void M432Reset(void) {
|
||||
EXPREGS[0] = 0;
|
||||
EXPREGS[1] = 0;
|
||||
EXPREGS[2] = (EXPREGS[2] +1) & 3;
|
||||
EXPREGS[2]++;
|
||||
MMC3RegReset();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,79 +24,51 @@
|
||||
|
||||
#include "mapinc.h"
|
||||
|
||||
static uint8 inner_bank, outer_bank, mode;
|
||||
static uint8 latch, reg[2];
|
||||
|
||||
static SFORMAT StateRegs[] =
|
||||
{
|
||||
{ &inner_bank, 1, "INB0" },
|
||||
{ &outer_bank, 1, "OUTB" },
|
||||
{ &mode, 1, "MODE" },
|
||||
{ &latch, 1, "LATC" },
|
||||
{ reg, 2, "REGS" },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static void Sync(void) {
|
||||
uint8 bbank = (mode & 4) ? 0 : (inner_bank & 7);
|
||||
uint8 bank = outer_bank | bbank;
|
||||
uint8 preg[2];
|
||||
|
||||
/* 0: NROM-128: Same inner/outer 16 KiB bank at CPU $8000-$BFFF
|
||||
* and $C000-$FFFF
|
||||
* 1: NROM-256: 32 kiB bank at CPU $8000-$FFFF (Selected inner/outer bank SHR 1)
|
||||
* 2: UNROM: Inner/outer bank at CPU $8000-BFFF,
|
||||
* fixed inner bank 7 within outer bank at $C000-$FFFF
|
||||
* 3: Unknown
|
||||
*
|
||||
* The combined inner/outer bank is simply the inner bank, selected by the
|
||||
* latch at $8000-$FFFF (or 0 if the latch is disabled) ORed with the
|
||||
* outer bank selected by $6001, without any bit shifting.
|
||||
*/
|
||||
|
||||
preg[0] = bank;
|
||||
preg[1] = 0;
|
||||
|
||||
switch (mode & 3) {
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
preg[1] = bank | ((mode & 1) ? 1 : 0);
|
||||
break;
|
||||
case 0x02:
|
||||
preg[1] = outer_bank | 7;
|
||||
case 0x03:
|
||||
break;
|
||||
if (reg[0] &2) { /* UNROM */
|
||||
setprg16(0x8000, latch &7 | reg[1] &~7);
|
||||
setprg16(0xC000, 7 | reg[1] &~7);
|
||||
} else
|
||||
if (reg[0] &1) /* NROM-256 */
|
||||
setprg32(0x8000, reg[1] >>1);
|
||||
else { /* NROM-128 */
|
||||
setprg16(0x8000, reg[1]);
|
||||
setprg16(0xC000, reg[1]);
|
||||
}
|
||||
|
||||
SetupCartCHRMapping(0, CHRptr[0], 0x2000, !(reg[0] &4)); /* CHR-RAM write-protect */
|
||||
setchr8(0);
|
||||
setprg16(0x8000, preg[0]);
|
||||
setprg16(0xC000, preg[1]);
|
||||
setmirror(((mode & 8) >> 3) ^ 1);
|
||||
setmirror(!(reg[0] &8));
|
||||
}
|
||||
|
||||
static DECLFW(Write0) {
|
||||
mode = V;
|
||||
static DECLFW(WriteReg) {
|
||||
reg[A &1] =V;
|
||||
Sync();
|
||||
}
|
||||
|
||||
static DECLFW(Write1) {
|
||||
outer_bank = V;
|
||||
Sync();
|
||||
}
|
||||
|
||||
static DECLFW(Write8) {
|
||||
inner_bank = V;
|
||||
static DECLFW(WriteLatch) {
|
||||
latch = V;
|
||||
Sync();
|
||||
}
|
||||
|
||||
static void BMC60311CPower(void) {
|
||||
inner_bank = outer_bank = mode = 0;
|
||||
latch =reg[0] =reg[1] =0;
|
||||
Sync();
|
||||
SetReadHandler(0x8000, 0xFFFF, CartBR);
|
||||
SetWriteHandler(0x6000, 0x6000, Write0);
|
||||
SetWriteHandler(0x6001, 0x6001, Write1);
|
||||
SetWriteHandler(0x8000, 0xFFFF, Write8);
|
||||
SetWriteHandler(0x6000, 0x6001, WriteReg);
|
||||
SetWriteHandler(0x8000, 0xFFFF, WriteLatch);
|
||||
}
|
||||
|
||||
static void BMC60311CReset(void) {
|
||||
inner_bank = outer_bank = mode = 0;
|
||||
latch =reg[0] =reg[1] =0;
|
||||
Sync();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,26 +27,25 @@
|
||||
#include "mmc3.h"
|
||||
|
||||
static void BMCGN26CW(uint32 A, uint8 V) {
|
||||
uint32 base = (EXPREGS[0] & 0x03) << 7;
|
||||
setchr1(A, base | (V & 0xFF));
|
||||
int chrAND = EXPREGS[0]&0x04? 0xFF: 0x7F;
|
||||
int chrOR = EXPREGS[0] <<7;
|
||||
setchr1(A, V &chrAND | chrOR &~chrAND);
|
||||
}
|
||||
|
||||
static void BMCGN26PW(uint32 A, uint8 V) {
|
||||
/* Re-ordered -> 0:SF4 1:Contra Force 2:Revolution Hero */
|
||||
uint32 table[] = { 0, 0, 1, 2 };
|
||||
uint32 base = table[(EXPREGS[0] & 0x03)];
|
||||
|
||||
if (EXPREGS[0] & 4) {
|
||||
if (A == 0x8000)
|
||||
setprg32(0x8000, (base << 2) | (V >> 2));
|
||||
setprg32(0x8000, (EXPREGS[0] << 2) | (V >> 2));
|
||||
} else
|
||||
setprg8(A, (base << 4) | (V & 0x0F));
|
||||
setprg8(A, (EXPREGS[0] << 4) | (V & 0x0F));
|
||||
}
|
||||
|
||||
static DECLFW(BMCGN26Write) {
|
||||
EXPREGS[0] = A & 0x0F;
|
||||
FixMMC3PRG(MMC3_cmd);
|
||||
FixMMC3CHR(MMC3_cmd);
|
||||
if (A001B &0x80 && ~A001B &0x40) {
|
||||
EXPREGS[0] = A;
|
||||
FixMMC3PRG(MMC3_cmd);
|
||||
FixMMC3CHR(MMC3_cmd);
|
||||
}
|
||||
}
|
||||
|
||||
static void BMCGN26Reset(void) {
|
||||
@@ -56,7 +55,7 @@ static void BMCGN26Reset(void) {
|
||||
|
||||
static void BMCGN26Power(void) {
|
||||
GenMMC3Power();
|
||||
SetWriteHandler(0x6800, 0x68FF, BMCGN26Write);
|
||||
SetWriteHandler(0x6000, 0x7FFF, BMCGN26Write);
|
||||
}
|
||||
|
||||
void BMCGN26_Init(CartInfo *info) {
|
||||
|
||||
@@ -474,27 +474,33 @@ void Mapper44_Init(CartInfo *info) {
|
||||
/* ---------------------------- Mapper 45 ------------------------------- */
|
||||
|
||||
static void M45CW(uint32 A, uint8 V) {
|
||||
if (!UNIFchrrama) {
|
||||
uint32 NV = V;
|
||||
if (EXPREGS[2] & 8)
|
||||
NV &= (1 << ((EXPREGS[2] & 7) + 1)) - 1;
|
||||
else
|
||||
if (EXPREGS[2])
|
||||
NV &= 0; /* hack ;( don't know exactly how it should be */
|
||||
NV |= EXPREGS[0] | ((EXPREGS[2] & 0xF0) << 4);
|
||||
setchr1(A, NV);
|
||||
} else
|
||||
/* setchr8(0); */ /* i don't know what cart need this, but a new one need other lol */
|
||||
if (CHRsize[0] ==8192)
|
||||
setchr1(A, V);
|
||||
else {
|
||||
int chrAND =0xFF >>(~EXPREGS[2] &0xF);
|
||||
int chrOR =EXPREGS[0] | EXPREGS[2] <<4 &0xF00;
|
||||
setchr1(A, V &chrAND | chrOR &~chrAND);
|
||||
}
|
||||
}
|
||||
|
||||
static DECLFR(M45ReadOB) {
|
||||
return X.DB;
|
||||
}
|
||||
|
||||
static void M45PW(uint32 A, uint8 V) {
|
||||
uint32 MV = V & ((EXPREGS[3] & 0x3F) ^ 0x3F);
|
||||
MV |= EXPREGS[1];
|
||||
if(UNIFchrrama)
|
||||
MV |= ((EXPREGS[2] & 0x40) << 2);
|
||||
setprg8(A, MV);
|
||||
/* FCEU_printf("1:%02x 2:%02x 3:%02x A=%04x V=%03x\n",EXPREGS[1],EXPREGS[2],EXPREGS[3],A,MV); */
|
||||
int prgAND =~EXPREGS[3] &0x3F;
|
||||
int prgOR =EXPREGS[1] | EXPREGS[2] <<2 &0x300;
|
||||
setprg8(A, V &prgAND | prgOR &~prgAND);
|
||||
|
||||
/* Some multicarts select between five different menus by connecting one of the higher address lines to PRG /CE.
|
||||
The menu code selects between menus by checking which of the higher address lines disables PRG-ROM when set. */
|
||||
if (PRGsize[0] <0x200000 && EXPREGS[5] ==1 && EXPREGS[1] &0x80 ||
|
||||
PRGsize[0] <0x200000 && EXPREGS[5] ==2 && EXPREGS[2] &0x40 ||
|
||||
PRGsize[0] <0x100000 && EXPREGS[5] ==3 && EXPREGS[1] &0x40 ||
|
||||
PRGsize[0] <0x100000 && EXPREGS[5] ==4 && EXPREGS[2] &0x20)
|
||||
SetReadHandler(0x8000, 0xFFFF, M45ReadOB);
|
||||
else
|
||||
SetReadHandler(0x8000, 0xFFFF, CartBR);
|
||||
}
|
||||
|
||||
static DECLFW(M45Write) {
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
#include "mapinc.h"
|
||||
|
||||
static uint8 submapper;
|
||||
|
||||
/* General Purpose Registers */
|
||||
static uint8 cpu410x[16], ppu201x[16], apu40xx[64];
|
||||
|
||||
@@ -35,9 +37,6 @@ static uint8 IRQCount, IRQa, IRQReload;
|
||||
#define IRQLatch cpu410x[0x1] /* accc cccc, a = 0, AD12 switching, a = 1, HSYNC switching */
|
||||
|
||||
/* MMC3 Registers */
|
||||
static uint8 inv_hack = 0; /* some OneBus Systems have swapped PRG reg commans in MMC3 inplementation,
|
||||
* trying to autodetect unusual behavior, due not to add a new mapper.
|
||||
*/
|
||||
#define mmc3cmd cpu410x[0x5] /* pcv- ----, p - program swap, c - video swap, v - internal VRAM enable */
|
||||
#define mirror cpu410x[0x6] /* ---- ---m, m = 0 - H, m = 1 - V */
|
||||
|
||||
@@ -65,6 +64,8 @@ static SFORMAT StateRegs[] =
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static uint8 *WRAM;
|
||||
|
||||
static void PSync(void) {
|
||||
uint8 bankmode = cpu410x[0xb] & 7;
|
||||
uint8 mask = (bankmode == 0x7) ? (0xff) : (0x3f >> bankmode);
|
||||
@@ -77,8 +78,8 @@ static void PSync(void) {
|
||||
uint8 bank2 = (cpu410x[0xb] & 0x40)?(cpu410x[0x9]):(~1);
|
||||
uint8 bank3 = ~0;
|
||||
#endif
|
||||
uint8 bank0 = cpu410x[0x7 ^ inv_hack];
|
||||
uint8 bank1 = cpu410x[0x8 ^ inv_hack];
|
||||
uint8 bank0 = cpu410x[0x7];
|
||||
uint8 bank1 = cpu410x[0x8];
|
||||
uint8 bank2 = (cpu410x[0xb] & 0x40) ? (cpu410x[0x9]) : (~1);
|
||||
uint8 bank3 = ~0;
|
||||
|
||||
@@ -124,29 +125,91 @@ static void Sync(void) {
|
||||
CSync();
|
||||
}
|
||||
|
||||
static const uint8 cpuMangle[16][4] = {
|
||||
{ 0, 1, 2, 3 }, /* Submapper 0: Normal */
|
||||
{ 0, 1, 2, 3 }, /* Submapper 1: Waixing VT03 */
|
||||
{ 1, 0, 2, 3 }, /* Submapper 2: Trump Grand */
|
||||
{ 0, 1, 2, 3 }, /* Submapper 3: Zechess */
|
||||
{ 0, 1, 2, 3 }, /* Submapper 4: Qishenglong */
|
||||
{ 0, 1, 2, 3 }, /* Submapper 5: Waixing VT02 */
|
||||
{ 0, 1, 2, 3 }, /* Submapper 6: unused so far */
|
||||
{ 0, 1, 2, 3 }, /* Submapper 7: unused so far */
|
||||
{ 0, 1, 2, 3 }, /* Submapper 8: unused so far */
|
||||
{ 0, 1, 2, 3 }, /* Submapper 9: unused so far */
|
||||
{ 0, 1, 2, 3 }, /* Submapper A: unused so far */
|
||||
{ 0, 1, 2, 3 }, /* Submapper B: unused so far */
|
||||
{ 0, 1, 2, 3 }, /* Submapper C: unused so far */
|
||||
{ 0, 1, 2, 3 }, /* Submapper D: Cube Tech (CPU opcode encryption only) */
|
||||
{ 0, 1, 2, 3 }, /* Submapper E: Karaoto (CPU opcode encryption only) */
|
||||
{ 0, 1, 2, 3 } /* Submapper F: Jungletac (CPU opcode encryption only) */
|
||||
};
|
||||
static DECLFW(UNLOneBusWriteCPU410X) {
|
||||
/* FCEU_printf("CPU %04x:%04x\n",A,V); */
|
||||
switch (A & 0xf) {
|
||||
A &=0xF;
|
||||
switch (A) {
|
||||
case 0x1: IRQLatch = V & 0xfe; break; /* íå ïî äàòàøèòó */
|
||||
case 0x2: IRQReload = 1; break;
|
||||
case 0x3: X6502_IRQEnd(FCEU_IQEXT); IRQa = 0; break;
|
||||
case 0x4: IRQa = 1; break;
|
||||
default:
|
||||
cpu410x[A & 0xf] = V;
|
||||
if (A >=0x7 && A <=0xA) A =0x7 +cpuMangle[submapper][A -0x7];
|
||||
cpu410x[A] = V;
|
||||
Sync();
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8 ppuMangle[16][6] = {
|
||||
{ 0, 1, 2, 3, 4, 5 }, /* Submapper 0: Normal */
|
||||
{ 1, 0, 5, 4, 3, 2 }, /* Submapper 1: Waixing VT03 */
|
||||
{ 0, 1, 2, 3, 4, 5 }, /* Submapper 2: Trump Grand */
|
||||
{ 5, 4, 3, 2, 0, 1 }, /* Submapper 3: Zechess */
|
||||
{ 2, 5, 0, 4, 3, 1 }, /* Submapper 4: Qishenglong */
|
||||
{ 1, 0, 5, 4, 3, 2 }, /* Submapper 5: Waixing VT02 */
|
||||
{ 0, 1, 2, 3, 4, 5 }, /* Submapper 6: unused so far */
|
||||
{ 0, 1, 2, 3, 4, 5 }, /* Submapper 7: unused so far */
|
||||
{ 0, 1, 2, 3, 4, 5 }, /* Submapper 8: unused so far */
|
||||
{ 0, 1, 2, 3, 4, 5 }, /* Submapper 9: unused so far */
|
||||
{ 0, 1, 2, 3, 4, 5 }, /* Submapper A: unused so far */
|
||||
{ 0, 1, 2, 3, 4, 5 }, /* Submapper B: unused so far */
|
||||
{ 0, 1, 2, 3, 4, 5 }, /* Submapper C: unused so far */
|
||||
{ 0, 1, 2, 3, 4, 5 }, /* Submapper D: Cube Tech (CPU opcode encryption only) */
|
||||
{ 0, 1, 2, 3, 4, 5 }, /* Submapper E: Karaoto (CPU opcode encryption only) */
|
||||
{ 0, 1, 2, 3, 4, 5 } /* Submapper F: Jungletac (CPU opcode encryption only) */
|
||||
};
|
||||
static DECLFW(UNLOneBusWritePPU201X) {
|
||||
/* FCEU_printf("PPU %04x:%04x\n",A,V); */
|
||||
ppu201x[A & 0x0f] = V;
|
||||
A &=0x0F;
|
||||
if (A >=2 && A <=7) A =2 +ppuMangle[submapper][A -2];
|
||||
ppu201x[A] = V;
|
||||
Sync();
|
||||
}
|
||||
|
||||
static const uint8 mmc3Mangle[16][8] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* Submapper 0: Normal */
|
||||
{ 5, 4, 3, 2, 1, 0, 6, 7 }, /* Submapper 1: Waixing VT03 */
|
||||
{ 0, 1, 2, 3, 4, 5, 7, 6 }, /* Submapper 2: Trump Grand */
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* Submapper 3: Zechess */
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* Submapper 4: Qishenglong */
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* Submapper 5: Waixing VT02 */
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* Submapper 6: unused so far */
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* Submapper 7: unused so far */
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* Submapper 8: unused so far */
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* Submapper 9: unused so far */
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* Submapper A: unused so far */
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* Submapper B: unused so far */
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* Submapper C: unused so far */
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* Submapper D: Cube Tech (CPU opcode encryption only) */
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, /* Submapper E: Karaoto (CPU opcode encryption only) */
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 } /* Submapper F: Jungletac (CPU opcode encryption only) */
|
||||
};
|
||||
static DECLFW(UNLOneBusWriteMMC3) {
|
||||
/* FCEU_printf("MMC %04x:%04x\n",A,V); */
|
||||
switch (A & 0xe001) {
|
||||
case 0x8000: mmc3cmd = (mmc3cmd & 0x38) | (V & 0xc7); Sync(); break;
|
||||
case 0x8000:
|
||||
V =V &0xF8 | mmc3Mangle[submapper][V &0x07];
|
||||
mmc3cmd = (mmc3cmd & 0x38) | (V & 0xc7);
|
||||
Sync();
|
||||
break;
|
||||
case 0x8001:
|
||||
{
|
||||
switch (mmc3cmd & 7) {
|
||||
@@ -263,11 +326,14 @@ static void UNLOneBusPower(void) {
|
||||
SetReadHandler(0x4000, 0x403f, UNLOneBusReadAPU40XX);
|
||||
SetWriteHandler(0x4000, 0x403f, UNLOneBusWriteAPU40XX);
|
||||
|
||||
SetReadHandler(0x8000, 0xFFFF, CartBR);
|
||||
SetReadHandler(0x6000, 0xFFFF, CartBR);
|
||||
SetWriteHandler(0x6000, 0x7FFF, CartBW);
|
||||
SetWriteHandler(0x2010, 0x201f, UNLOneBusWritePPU201X);
|
||||
SetWriteHandler(0x4100, 0x410f, UNLOneBusWriteCPU410X);
|
||||
SetWriteHandler(0x8000, 0xffff, UNLOneBusWriteMMC3);
|
||||
|
||||
FCEU_CheatAddRAM(8, 0x6000, WRAM);
|
||||
setprg8r(0x10, 0x6000, 0);
|
||||
Sync();
|
||||
}
|
||||
|
||||
@@ -285,16 +351,28 @@ static void StateRestore(int version) {
|
||||
Sync();
|
||||
}
|
||||
|
||||
void UNLOneBus_Close(void) {
|
||||
if (WRAM)
|
||||
FCEU_gfree(WRAM);
|
||||
WRAM = NULL;
|
||||
}
|
||||
|
||||
void UNLOneBus_Init(CartInfo *info) {
|
||||
info->Power = UNLOneBusPower;
|
||||
info->Reset = UNLOneBusReset;
|
||||
info->Close = UNLOneBus_Close;
|
||||
|
||||
if (((*(uint32*)&(info->MD5)) == 0x305fcdc3) || /* PowerJoy Supermax Carts */
|
||||
((*(uint32*)&(info->MD5)) == 0x6abfce8e))
|
||||
inv_hack = 0xf;
|
||||
if (info->iNES2)
|
||||
submapper =info->submapper;
|
||||
else
|
||||
submapper =(((*(uint32*)&(info->MD5)) == 0x305fcdc3) || ((*(uint32*)&(info->MD5)) == 0x6abfce8e))? 2: 0; /* PowerJoy Supermax Carts */
|
||||
|
||||
GameHBIRQHook = UNLOneBusIRQHook;
|
||||
MapIRQHook = UNLOneBusCpuHook;
|
||||
GameStateRestore = StateRestore;
|
||||
AddExState(&StateRegs, ~0, 0, 0);
|
||||
|
||||
WRAM = (uint8*)FCEU_gmalloc(8192);
|
||||
SetupCartPRGMapping(0x10, WRAM, 8192, 1);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user