mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-23 23:30:22 +00:00
Various fixes
This commit is contained in:
parent
a2c49a1f89
commit
d0bf683657
@ -1002,7 +1002,7 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
||||
case DE => changesRegister(D) || changesRegister(E)
|
||||
case IX => changesRegister(IXH) || changesRegister(IXL)
|
||||
case IY => changesRegister(IYH) || changesRegister(IYL)
|
||||
case AF => ???
|
||||
case AF => true
|
||||
case IMM_8 | IMM_16 => false
|
||||
case MEM_ABS_8 | MEM_ABS_16 | MEM_DE | MEM_HL | MEM_BC | MEM_IX_D | MEM_IY_D | SP => ???
|
||||
case _ =>
|
||||
|
@ -496,6 +496,9 @@ object AlwaysGoodI80Optimizations {
|
||||
shallowerStack(code.tail.init)
|
||||
}
|
||||
}),
|
||||
//37
|
||||
(Elidable & HasOpcode(PUSH) & HasRegisterParam(AF)) ~
|
||||
(Elidable & HasOpcode(POP) & HasRegisterParam(AF)) ~~> (_.tail.init),
|
||||
)
|
||||
|
||||
private def shallowerStack(lines: List[ZLine]): List[ZLine] = lines match {
|
||||
|
@ -974,6 +974,11 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
|
||||
case sot: StackOffsetThing =>
|
||||
List(calculateStackAddressToD(ctx, sot.offset), List(MLine.tfr(M6809Register.A, M6809Register.B)))
|
||||
}
|
||||
case e:DerefExpression =>
|
||||
List.tabulate(targetSize)(i =>
|
||||
if (i == 0) compileAddressToX(ctx, e) :+ MLine.indexedX(LDB, 0)
|
||||
else List(MLine.indexedX(LDB, i))
|
||||
)
|
||||
case e:FunctionCallExpression =>
|
||||
ctx.env.maybeGet[NormalFunction](e.functionName) match {
|
||||
case Some(function) =>
|
||||
|
@ -446,7 +446,7 @@ object BuiltIns {
|
||||
AssemblyLine.implied(TAX),
|
||||
AssemblyLine.label(labelRepeat)) ++ singleShift ++ List(
|
||||
AssemblyLine.implied(DEX),
|
||||
AssemblyLine.relative(BEQ, labelRepeat))
|
||||
AssemblyLine.relative(BNE, labelRepeat))
|
||||
case _ =>
|
||||
val labelSkip = ctx.nextLabel("ss")
|
||||
val labelRepeat = ctx.nextLabel("sr")
|
||||
@ -455,7 +455,7 @@ object BuiltIns {
|
||||
AssemblyLine.relative(BEQ, labelSkip),
|
||||
AssemblyLine.label(labelRepeat)) ++ singleShift ++ List(
|
||||
AssemblyLine.implied(DEX),
|
||||
AssemblyLine.relative(BEQ, labelRepeat),
|
||||
AssemblyLine.relative(BNE, labelRepeat),
|
||||
AssemblyLine.label(labelSkip))
|
||||
}
|
||||
}))
|
||||
|
@ -113,17 +113,17 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
|
||||
def compileToHL(ctx: CompilationContext, expression: Expression): List[ZLine] = compile(ctx, expression, ZExpressionTarget.HL)
|
||||
|
||||
def compileDerefPointer(ctx: CompilationContext, expression: DerefExpression): List[ZLine] = {
|
||||
def compileDerefPointer(ctx: CompilationContext, expression: DerefExpression, extraOffset: Int = 0): List[ZLine] = {
|
||||
import ZRegister._
|
||||
val innerPart = compileToHL(ctx, expression.inner)
|
||||
innerPart match {
|
||||
case List(ZLine0(LD_16, TwoRegisters(HL, IMM_16), c)) =>
|
||||
List(ZLine(LD_16, TwoRegisters(HL, IMM_16), c + expression.offset))
|
||||
List(ZLine(LD_16, TwoRegisters(HL, IMM_16), c + expression.offset + extraOffset))
|
||||
case _ =>
|
||||
innerPart ++ (expression.offset match {
|
||||
innerPart ++ ((expression.offset + extraOffset) match {
|
||||
case 0 => Nil
|
||||
case i if i < 5 => List.fill(i)(ZLine.register(INC_16, ZRegister.HL)) // TODO: a better threshold
|
||||
case _ => List(ZLine.ldImm8(ZRegister.C, expression.offset), ZLine.ldImm8(ZRegister.B, 0), ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
case i if i > 0 && i < 5 => List.fill(i)(ZLine.register(INC_16, ZRegister.HL)) // TODO: a better threshold
|
||||
case n => List(ZLine.ldImm8(ZRegister.C, n & 0xff), ZLine.ldImm8(ZRegister.B, n >> 8), ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1355,12 +1355,12 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
|
||||
def calculateAddressToAppropriatePointer(ctx: CompilationContext, expr: LhsExpression, forWriting: Boolean): Option[(LocalVariableAddressOperand, List[ZLine])] = {
|
||||
def calculateAddressToAppropriatePointer(ctx: CompilationContext, expr: LhsExpression, forWriting: Boolean, extraOffset: Int = 0): Option[(LocalVariableAddressOperand, List[ZLine])] = {
|
||||
val env = ctx.env
|
||||
expr match {
|
||||
case VariableExpression(name) =>
|
||||
env.get[Variable](name) match {
|
||||
case v:VariableInMemory => Some(LocalVariableAddressViaHL -> List(ZLine.ldImm16(ZRegister.HL, v.toAddress)))
|
||||
case v:VariableInMemory => Some(LocalVariableAddressViaHL -> List(ZLine.ldImm16(ZRegister.HL, v.toAddress + extraOffset)))
|
||||
case v:StackVariable =>
|
||||
if (ctx.options.flag(CompilationFlag.UseIxForStack)){
|
||||
Some(LocalVariableAddressViaIX(v.baseOffset) -> Nil)
|
||||
@ -1370,8 +1370,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
Some(LocalVariableAddressViaHL -> calculateStackAddressToHL(ctx, v))
|
||||
}
|
||||
}
|
||||
case i:IndexedExpression => Some(LocalVariableAddressViaHL -> calculateAddressToHL(ctx, i, forWriting))
|
||||
case i:DerefExpression => Some(LocalVariableAddressViaHL -> compileDerefPointer(ctx, i))
|
||||
case i:IndexedExpression => Some(LocalVariableAddressViaHL -> calculateAddressToHL(ctx, i, forWriting, extraOffset))
|
||||
case i:DerefExpression => Some(LocalVariableAddressViaHL -> compileDerefPointer(ctx, i, extraOffset))
|
||||
case _:SeparateBytesExpression => None
|
||||
case _ => ???
|
||||
}
|
||||
@ -1391,11 +1391,11 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
|
||||
def calculateAddressToHL(ctx: CompilationContext, i: IndexedExpression, forWriting: Boolean): List[ZLine] = {
|
||||
def calculateAddressToHL(ctx: CompilationContext, i: IndexedExpression, forWriting: Boolean, extraOffset: Int = 0): List[ZLine] = {
|
||||
val env = ctx.env
|
||||
val pointy = env.getPointy(i.name)
|
||||
AbstractExpressionCompiler.checkIndexType(ctx, pointy, i.index)
|
||||
val elementSize = pointy.elementType.size
|
||||
val elementSize = pointy.elementType.alignedSize
|
||||
val logElemSize = elementSize match {
|
||||
case 1 => 0
|
||||
case 2 => 1
|
||||
@ -1409,9 +1409,9 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
ctx.log.error("Writing to a constant array", i.position)
|
||||
}
|
||||
env.evalVariableAndConstantSubParts(i.index) match {
|
||||
case (None, offset) => List(ZLine.ldImm16(ZRegister.HL, (baseAddr + offset * elementSize).quickSimplify))
|
||||
case (None, offset) => List(ZLine.ldImm16(ZRegister.HL, (baseAddr + extraOffset + offset * elementSize).quickSimplify))
|
||||
case (Some(index), offset) =>
|
||||
val constantPart = (baseAddr + offset * elementSize).quickSimplify
|
||||
val constantPart = (baseAddr + extraOffset + offset * elementSize).quickSimplify
|
||||
if (getExpressionType(ctx, i.index).size == 1 && sizeInBytes.exists(_ < 256) && alignment == WithinPageAlignment) {
|
||||
compileToA(ctx, i.index) ++ List.fill(logElemSize)(ZLine.register(ADD, ZRegister.A)) ++ List(
|
||||
ZLine.imm8(ADD, constantPart.loByte),
|
||||
@ -1426,7 +1426,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
case VariablePointy(varAddr, _, _, _) =>
|
||||
env.eval(i.index) match {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
case Some(NumericConstant(0, _)) if extraOffset == 0 =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
List(ZLine.ldAbs16(ZRegister.HL, varAddr))
|
||||
} else {
|
||||
@ -1439,12 +1439,12 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
case _ =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
compileToBC(ctx, i.index) ++
|
||||
compileToBC(ctx, i.index #*# elementSize #+# extraOffset) ++
|
||||
List(ZLine.ldAbs16(ZRegister.HL, varAddr)) ++
|
||||
List.fill(elementSize)(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
} else {
|
||||
// TODO: is this reasonable?
|
||||
compileToBC(ctx, i.index) ++
|
||||
compileToBC(ctx, i.index #*# elementSize #+# extraOffset) ++
|
||||
List(
|
||||
ZLine.ldAbs8(ZRegister.A, varAddr),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
@ -1455,7 +1455,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
case _: StackVariablePointy =>
|
||||
compileToHL(ctx, VariableExpression(i.name).pos(i.position)) ++
|
||||
stashHLIfChanged(ctx, compileToBC(ctx, i.index)) ++
|
||||
stashHLIfChanged(ctx, compileToBC(ctx, i.index #*# elementSize #+# extraOffset)) ++
|
||||
List.fill(elementSize)(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
package millfork.compiler.z80
|
||||
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.z80.{NoRegisters, ZLine, ZOpcode}
|
||||
import millfork.assembly.z80.{LocalVariableAddressViaHL, LocalVariableAddressViaIX, LocalVariableAddressViaIY, NoRegisters, ZLine, ZOpcode}
|
||||
import millfork.compiler.CompilationContext
|
||||
import millfork.env.NumericConstant
|
||||
import millfork.error.ConsoleLogger
|
||||
import millfork.node._
|
||||
|
||||
import scala.collection.GenTraversableOnce
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
@ -244,44 +245,117 @@ object Z80Shifting {
|
||||
}
|
||||
|
||||
def compileLongShiftInPlace(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, size: Int, left: Boolean): List[ZLine] = {
|
||||
val extended = ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)
|
||||
val store = Z80ExpressionCompiler.compileByteStores(ctx, lhs, size, includeStep = false)
|
||||
val loadLeft = Z80ExpressionCompiler.compileByteReads(ctx, lhs, size, ZExpressionTarget.HL)
|
||||
val shiftOne = if (left) {
|
||||
loadLeft.zip(store).zipWithIndex.flatMap {
|
||||
case ((ld, st), ix) =>
|
||||
import ZOpcode._
|
||||
import ZRegister._
|
||||
val shiftByte =
|
||||
if (ix == 0) List(ZLine.register(ADD, A))
|
||||
else List(ZLine.implied(RLA))
|
||||
ld ++ shiftByte ++ st
|
||||
val extended = ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)
|
||||
val intel8080 = ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)
|
||||
val Some((loadRegisterOperand, loadSequence)) =
|
||||
Z80ExpressionCompiler.calculateAddressToAppropriatePointer(ctx, lhs, forWriting = true, extraOffset = if (left) 0 else size-1)
|
||||
val result = ListBuffer[ZLine]()
|
||||
result ++= loadSequence
|
||||
def appendShift(preserveHL: Boolean): Unit = {
|
||||
if (loadRegisterOperand == LocalVariableAddressViaHL && preserveHL) {
|
||||
result += ZLine.ld8(D, H)
|
||||
result += ZLine.ld8(E, L)
|
||||
}
|
||||
if (left) {
|
||||
for (ix <- 0 until size) {
|
||||
val shiftedOperand = loadRegisterOperand match {
|
||||
case LocalVariableAddressViaHL =>
|
||||
LocalVariableAddressViaHL
|
||||
case LocalVariableAddressViaIX(offset) =>
|
||||
LocalVariableAddressViaIX(offset + ix)
|
||||
case LocalVariableAddressViaIY(offset) =>
|
||||
LocalVariableAddressViaIY(offset + ix)
|
||||
}
|
||||
if (ix == 0) {
|
||||
if (extended) {
|
||||
result += ZLine.register(SLA, shiftedOperand)
|
||||
} else {
|
||||
result += ZLine.ld8(A, shiftedOperand)
|
||||
result += ZLine.register(ADD, A)
|
||||
result += ZLine.ld8(shiftedOperand, A)
|
||||
}
|
||||
} else {
|
||||
loadLeft.reverse.zip(store.reverse).zipWithIndex.flatMap {
|
||||
case ((ld, st), ix) =>
|
||||
import ZOpcode._
|
||||
import ZRegister._
|
||||
val shiftByte = if (ix == 0) {
|
||||
if (extended) List(ZLine.register(SRL, A))
|
||||
else List(ZLine.register(OR, A), ZLine.implied(RRA))
|
||||
} else List(ZLine.implied(RRA))
|
||||
ld ++ shiftByte ++ st
|
||||
if (extended) {
|
||||
result += ZLine.register(RL, shiftedOperand)
|
||||
} else {
|
||||
result += ZLine.ld8(A, shiftedOperand)
|
||||
result += ZLine.implied(RLA)
|
||||
result += ZLine.ld8(shiftedOperand, A)
|
||||
}
|
||||
}
|
||||
if (loadRegisterOperand == LocalVariableAddressViaHL && ix != size - 1) {
|
||||
result += ZLine.register(INC_16, HL)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (ix <- (size - 1).to(0, -1)) {
|
||||
val shiftedOperand = loadRegisterOperand match {
|
||||
case LocalVariableAddressViaHL =>
|
||||
LocalVariableAddressViaHL
|
||||
case LocalVariableAddressViaIX(offset) =>
|
||||
LocalVariableAddressViaIX(offset + ix)
|
||||
case LocalVariableAddressViaIY(offset) =>
|
||||
LocalVariableAddressViaIY(offset + ix)
|
||||
}
|
||||
if (ix == size - 1) {
|
||||
if (extended) {
|
||||
result += ZLine.register(SRL, shiftedOperand)
|
||||
} else {
|
||||
result += ZLine.ld8(A, shiftedOperand)
|
||||
result += ZLine.register(OR, A)
|
||||
result += ZLine.implied(RRA)
|
||||
result += ZLine.ld8(shiftedOperand, A)
|
||||
}
|
||||
} else {
|
||||
if (extended) {
|
||||
result += ZLine.register(RR, shiftedOperand)
|
||||
} else {
|
||||
result += ZLine.ld8(A, shiftedOperand)
|
||||
result += ZLine.implied(RRA)
|
||||
result += ZLine.ld8(shiftedOperand, A)
|
||||
}
|
||||
}
|
||||
if (loadRegisterOperand == LocalVariableAddressViaHL && ix != 0) {
|
||||
result += ZLine.register(DEC_16, HL)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (loadRegisterOperand == LocalVariableAddressViaHL && preserveHL) {
|
||||
if (intel8080) {
|
||||
result += ZLine.implied(EX_DE_HL)
|
||||
} else {
|
||||
result += ZLine.ld8(H, D)
|
||||
result += ZLine.ld8(L, E)
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.env.eval(rhs) match {
|
||||
case Some(NumericConstant(0, _)) => Nil
|
||||
case Some(NumericConstant(0, _)) => ()
|
||||
case Some(NumericConstant(n, _)) if n < 0 =>
|
||||
ctx.log.error("Negative shift amount", rhs.position) // TODO
|
||||
Nil
|
||||
case Some(NumericConstant(n, _)) =>
|
||||
List.fill(n.toInt)(shiftOne).flatten
|
||||
()
|
||||
case Some(NumericConstant(n, _)) if n < 3 => // TODO: more exact math on performance and size
|
||||
for(i <- 0 until n.toInt) {
|
||||
appendShift(i != n-1)
|
||||
}
|
||||
case _ =>
|
||||
val calcCount = calculateIterationCountPlus1(ctx, rhs)
|
||||
if (loadRegisterOperand == LocalVariableAddressViaHL) {
|
||||
result ++= Z80ExpressionCompiler.stashHLIfChanged(ctx, calcCount)
|
||||
} else {
|
||||
result ++= calcCount
|
||||
}
|
||||
val labelL = ctx.nextLabel("sh")
|
||||
val labelS = ctx.nextLabel("sh")
|
||||
calcCount ++ List(ZLine.jumpR(ctx, labelS), ZLine.label(labelL)) ++ shiftOne ++ List(ZLine.label(labelS)) ++ ZLine.djnz(ctx, labelL)
|
||||
result += ZLine.jumpR(ctx, labelS)
|
||||
result += ZLine.label(labelL)
|
||||
appendShift(true)
|
||||
result += ZLine.label(labelS)
|
||||
result ++= ZLine.djnz(ctx, labelL)
|
||||
}
|
||||
result.toList
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import millfork.env._
|
||||
import millfork.error.ConsoleLogger
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
@ -548,8 +549,35 @@ object ZBuiltIns {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ctx.log.error("Too complex left-hand-side expression", lhs.position)
|
||||
return Z80ExpressionCompiler.compile(ctx, lhs, ZExpressionTarget.NOTHING) ++ Z80ExpressionCompiler.compile(ctx, rhs, ZExpressionTarget.NOTHING)
|
||||
val target = Z80ExpressionCompiler.compileToHL(ctx, inner #+# offset)
|
||||
val sourceBytes = Z80ExpressionCompiler.compileByteReads(ctx, rhs, size, ZExpressionTarget.BC).map(l => Z80ExpressionCompiler.stashHLIfChanged(ctx, l))
|
||||
val result = ListBuffer[ZLine]()
|
||||
result ++= target
|
||||
if (opcodeFirst == SUB && decimal) {
|
||||
???
|
||||
} else if (opcodeFirst == SUB && !decimal) {
|
||||
for (i <- 0 until size) {
|
||||
if (i != 0) result += ZLine.register(PUSH, ZRegister.AF)
|
||||
result ++= sourceBytes(i)
|
||||
result += ZLine.ld8(ZRegister.E, ZRegister.A)
|
||||
if (i != 0) result += ZLine.register(POP, ZRegister.AF)
|
||||
result += ZLine.ld8(ZRegister.A, ZRegister.MEM_HL)
|
||||
result += ZLine.register(if (i == 0) opcodeFirst else opcodeLater, ZRegister.E)
|
||||
result += ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)
|
||||
if (i != size - 1) result += ZLine.register(INC_16, ZRegister.HL)
|
||||
}
|
||||
} else {
|
||||
for (i <- 0 until size) {
|
||||
result ++= sourceBytes(i)
|
||||
result += ZLine.register(if (i==0) opcodeFirst else opcodeLater, ZRegister.MEM_HL)
|
||||
if (decimal) result += ZLine.implied(DAA)
|
||||
result += ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)
|
||||
if (i != size -1) result += ZLine.register(INC_16, ZRegister.HL)
|
||||
}
|
||||
}
|
||||
return result.toList
|
||||
// ctx.log.error("Too complex left-hand-side expression", lhs.position)
|
||||
// return Z80ExpressionCompiler.compile(ctx, lhs, ZExpressionTarget.NOTHING) ++ Z80ExpressionCompiler.compile(ctx, rhs, ZExpressionTarget.NOTHING)
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
|
@ -465,29 +465,30 @@ class ArraySuite extends FunSuite with Matchers with AppendedClues {
|
||||
| a[0] = tmp
|
||||
| a[0] += 2
|
||||
| a[0] <<= 2
|
||||
| a[0] -= 7
|
||||
| }
|
||||
| noinline int32 f() = 5
|
||||
""".stripMargin) { m =>
|
||||
m.readLong(0xc000) should equal(28)
|
||||
m.readLong(0xc000) should equal(21)
|
||||
}
|
||||
}
|
||||
|
||||
test("Various large assignments involving arrays") {
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80)(
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80, Cpu.Motorola6809)(
|
||||
"""
|
||||
| array(int32) a[7] @$c000
|
||||
| void main () {
|
||||
| a[0] = 2
|
||||
| }
|
||||
""".stripMargin) { m => }
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80)(
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80, Cpu.Motorola6809)(
|
||||
"""
|
||||
| array(int32) a[7] @$c000
|
||||
| int32 main () {
|
||||
| return a[4]
|
||||
| }
|
||||
""".stripMargin) { m => }
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80)(
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80, Cpu.Motorola6809)(
|
||||
"""
|
||||
| array(int32) a[7] @$c000
|
||||
| noinline void f(byte i) {
|
||||
|
Loading…
Reference in New Issue
Block a user