crystal says Hello.
This commit is contained in:
parent
fa123e0354
commit
4135c4d2dc
@ -33,8 +33,16 @@ module Arm
|
|||||||
def word_load value , reg
|
def word_load value , reg
|
||||||
mov( :left => reg , :right => value )
|
mov( :left => reg , :right => value )
|
||||||
end
|
end
|
||||||
|
def string_load str_lit , reg
|
||||||
|
[ add( :left => "r#{reg}".to_sym , :extra => str_lit ) , #right is pc, implicit
|
||||||
|
#second arg is a hack to get the stringlength without coding
|
||||||
|
mov( :left => "r#{reg+1}".to_sym , :right => str_lit.length ) ]
|
||||||
|
end
|
||||||
|
|
||||||
def function_call call
|
def function_call call
|
||||||
raise "Not FunctionCall #{call.inspect}" unless call.is_a? Vm::FunctionCall
|
raise "Not FunctionCall #{call.inspect}" unless call.is_a? Vm::FunctionCall
|
||||||
|
call.args.each do | arg |
|
||||||
|
end
|
||||||
bl( :left => call.function )
|
bl( :left => call.function )
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -44,18 +52,28 @@ module Arm
|
|||||||
end
|
end
|
||||||
def main_exit
|
def main_exit
|
||||||
entry = Vm::Block.new("main_exit")
|
entry = Vm::Block.new("main_exit")
|
||||||
entry.add_code syscall(0)
|
entry.add_code syscall(1)
|
||||||
end
|
end
|
||||||
def function_entry f_name
|
def function_entry f_name
|
||||||
entry = Vm::Block.new("#{f_name}_entry")
|
entry = Vm::Block.new("#{f_name}_entry")
|
||||||
entry.add_code push( :left => :lr )
|
# entry.add_code push( :left => :lr )
|
||||||
end
|
end
|
||||||
def function_exit f_name
|
def function_exit f_name
|
||||||
entry = Vm::Block.new("#{f_name}_exit")
|
entry = Vm::Block.new("#{f_name}_exit")
|
||||||
entry.add_code pop( :left => :pc )
|
entry.add_code mov( :left => :pc , :right => :lr )
|
||||||
end
|
end
|
||||||
|
def putstring
|
||||||
|
put = Vm::Block.new("putstring_code")
|
||||||
|
# should be another level of indirection, ie write(io,str)
|
||||||
|
put.add_code mov( :left => :r2 , :right => :r1 )
|
||||||
|
put.add_code mov( :left => :r1 , :right => :r0 )
|
||||||
|
put.add_code mov( :left => :r0 , :right => 1 ) #stdout
|
||||||
|
put.add_code syscall(4)
|
||||||
|
end
|
||||||
|
private
|
||||||
def syscall num
|
def syscall num
|
||||||
[mov( :left => :r7 , :right => num ) , swi( :left => 0 )]
|
[mov( :left => :r7 , :right => num ) , swi( :left => 0 )]
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -4,11 +4,6 @@ module Arm
|
|||||||
module LogicHelper
|
module LogicHelper
|
||||||
# ADDRESSING MODE 1
|
# ADDRESSING MODE 1
|
||||||
# Logic ,Maths, Move and compare instructions (last three below)
|
# Logic ,Maths, Move and compare instructions (last three below)
|
||||||
# Build representation for source value
|
|
||||||
def build
|
|
||||||
@rn = @args[1]
|
|
||||||
do_build @args[2]
|
|
||||||
end
|
|
||||||
|
|
||||||
# arm intrucioons are pretty sensible, and always 4 bytes (thumb not supported)
|
# arm intrucioons are pretty sensible, and always 4 bytes (thumb not supported)
|
||||||
def length
|
def length
|
||||||
@ -21,6 +16,7 @@ module Arm
|
|||||||
# do pc relative addressing with the difference to the instuction
|
# do pc relative addressing with the difference to the instuction
|
||||||
# 8 is for the funny pipeline adjustment (ie oc pointing to fetch and not execute)
|
# 8 is for the funny pipeline adjustment (ie oc pointing to fetch and not execute)
|
||||||
arg = Arm::NumLiteral.new( arg.position - self.position - 8 )
|
arg = Arm::NumLiteral.new( arg.position - self.position - 8 )
|
||||||
|
@rn = :pc
|
||||||
end
|
end
|
||||||
if( arg.is_a? Fixnum ) #HACK to not have to change the code just now
|
if( arg.is_a? Fixnum ) #HACK to not have to change the code just now
|
||||||
arg = Arm::NumLiteral.new( arg )
|
arg = Arm::NumLiteral.new( arg )
|
||||||
@ -36,7 +32,7 @@ module Arm
|
|||||||
else
|
else
|
||||||
raise "cannot fit numeric literal argument in operand #{arg}"
|
raise "cannot fit numeric literal argument in operand #{arg}"
|
||||||
end
|
end
|
||||||
elsif (arg.is_a?(Arm::Register))
|
elsif (arg.is_a?(Symbol))
|
||||||
@operand = arg
|
@operand = arg
|
||||||
@i = 0
|
@i = 0
|
||||||
elsif (arg.is_a?(Arm::Shift))
|
elsif (arg.is_a?(Arm::Shift))
|
||||||
@ -64,7 +60,7 @@ module Arm
|
|||||||
|
|
||||||
@operand = rm_ref | (shift_op << 4) | (shift_imm << 4+3)
|
@operand = rm_ref | (shift_op << 4) | (shift_imm << 4+3)
|
||||||
else
|
else
|
||||||
raise "invalid operand argument #{arg.inspect}"
|
raise "invalid operand argument #{arg.inspect} , #{inspect}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -87,7 +83,7 @@ module Arm
|
|||||||
include LogicHelper
|
include LogicHelper
|
||||||
|
|
||||||
def initialize(options)
|
def initialize(options)
|
||||||
super(options)
|
super(options)
|
||||||
@update_status_flag = 0
|
@update_status_flag = 0
|
||||||
@condition_code = :al
|
@condition_code = :al
|
||||||
@opcode = options[:opcode]
|
@opcode = options[:opcode]
|
||||||
@ -96,9 +92,14 @@ module Arm
|
|||||||
|
|
||||||
@rn = nil
|
@rn = nil
|
||||||
@i = 0
|
@i = 0
|
||||||
@rd = args[0]
|
@rd = @args[0]
|
||||||
end
|
end
|
||||||
attr_accessor :i, :rn, :rd
|
attr_accessor :i, :rn, :rd
|
||||||
|
# Build representation for source value
|
||||||
|
def build
|
||||||
|
@rn = @args[1]
|
||||||
|
do_build @args[2]
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
class CompareInstruction < Vm::CompareInstruction
|
class CompareInstruction < Vm::CompareInstruction
|
||||||
|
@ -7,16 +7,22 @@ module Arm
|
|||||||
class MemoryInstruction < Vm::MemoryInstruction
|
class MemoryInstruction < Vm::MemoryInstruction
|
||||||
include Arm::Constants
|
include Arm::Constants
|
||||||
|
|
||||||
def initialize(opcode , condition_code , update_status , args)
|
def initialize(options)
|
||||||
super(opcode , condition_code , update_status , args)
|
super(options)
|
||||||
|
@update_status_flag = 0
|
||||||
|
@condition_code = :al
|
||||||
|
@opcode = options[:opcode]
|
||||||
|
@args = [options[:left] , options[:right] ]
|
||||||
|
@operand = 0
|
||||||
|
|
||||||
@i = 0 #I flag (third bit)
|
@i = 0 #I flag (third bit)
|
||||||
@pre_post_index = 0 #P flag
|
@pre_post_index = 0 #P flag
|
||||||
@add_offset = 0 #U flag
|
@add_offset = 0 #U flag
|
||||||
@byte_access = opcode.to_s[-1] == "b" ? 1 : 0 #B (byte) flag
|
@byte_access = opcode.to_s[-1] == "b" ? 1 : 0 #B (byte) flag
|
||||||
@w = 0 #W flag
|
@w = 0 #W flag
|
||||||
@is_load = opcode.to_s[0] == "l" ? 1 : 0 #L (load) flag
|
@is_load = opcode.to_s[0] == "l" ? 1 : 0 #L (load) flag
|
||||||
@rn = reg "r0" # register zero = zero bit pattern
|
@rn = :r0 # register zero = zero bit pattern
|
||||||
@rd = reg "r0" # register zero = zero bit pattern
|
@rd = :r0 # register zero = zero bit pattern
|
||||||
end
|
end
|
||||||
attr_accessor :i, :pre_post_index, :add_offset,
|
attr_accessor :i, :pre_post_index, :add_offset,
|
||||||
:byte_access, :w, :is_load, :rn, :rd
|
:byte_access, :w, :is_load, :rn, :rd
|
||||||
@ -29,17 +35,17 @@ module Arm
|
|||||||
# Build representation for target address
|
# Build representation for target address
|
||||||
def build
|
def build
|
||||||
if( @is_load )
|
if( @is_load )
|
||||||
@rd = args[0]
|
@rd = @args[0]
|
||||||
arg = args[1]
|
arg = @args[1]
|
||||||
else #store
|
else #store
|
||||||
@rd = args[1]
|
@rd = @args[1]
|
||||||
arg = args[0]
|
arg = @args[0]
|
||||||
end
|
end
|
||||||
#str / ldr are _serious instructions. With BIG possibilities not half are implemented
|
#str / ldr are _serious instructions. With BIG possibilities not half are implemented
|
||||||
if (arg.is_a?(Arm::Register))
|
if (arg.is_a?(Symbol)) #symbol is register
|
||||||
@rn = arg
|
@rn = arg
|
||||||
if(arg.offset != 0)
|
if options[:offset]
|
||||||
@operand = arg.offset
|
@operand = options[:offset]
|
||||||
if (@operand < 0)
|
if (@operand < 0)
|
||||||
@add_offset = 0
|
@add_offset = 0
|
||||||
#TODO test/check/understand
|
#TODO test/check/understand
|
||||||
@ -51,6 +57,13 @@ module Arm
|
|||||||
raise "reference offset too large/small (max 4095) #{arg} #{inspect}"
|
raise "reference offset too large/small (max 4095) #{arg} #{inspect}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
elsif (arg.is_a?(Vm::StringLiteral)) #use pc relative
|
||||||
|
@rn = :pc
|
||||||
|
@operand = arg.position - self.position - 8 #stringtable is after code
|
||||||
|
@add_offset = 1
|
||||||
|
if (@operand.abs > 4095)
|
||||||
|
raise "reference offset too large/small (max 4095) #{arg} #{inspect}"
|
||||||
|
end
|
||||||
elsif (arg.is_a?(Arm::Label) or arg.is_a?(Arm::NumLiteral))
|
elsif (arg.is_a?(Arm::Label) or arg.is_a?(Arm::NumLiteral))
|
||||||
@pre_post_index = 1
|
@pre_post_index = 1
|
||||||
@rn = pc
|
@rn = pc
|
||||||
@ -69,9 +82,9 @@ module Arm
|
|||||||
@add_offset = 1
|
@add_offset = 1
|
||||||
@pre_post_index = 1
|
@pre_post_index = 1
|
||||||
instuction_class = 0b01 # OPC_MEMORY_ACCESS
|
instuction_class = 0b01 # OPC_MEMORY_ACCESS
|
||||||
val = operand
|
val = @operand.is_a?(Symbol) ? reg_code(@operand) : @operand
|
||||||
val |= (rd.bits << 12 )
|
val |= (reg_code(rd) << 12 )
|
||||||
val |= (rn.bits << 12+4) #16
|
val |= (reg_code(rn) << 12+4) #16
|
||||||
val |= (is_load << 12+4 +4)
|
val |= (is_load << 12+4 +4)
|
||||||
val |= (w << 12+4 +4+1)
|
val |= (w << 12+4 +4+1)
|
||||||
val |= (byte_access << 12+4 +4+1+1)
|
val |= (byte_access << 12+4 +4+1+1)
|
||||||
@ -81,6 +94,7 @@ module Arm
|
|||||||
val |= (instuction_class<<12+4 +4+1+1+1+1 +1+1)
|
val |= (instuction_class<<12+4 +4+1+1+1+1 +1+1)
|
||||||
val |= (cond_bit_code << 12+4 +4+1+1+1+1 +1+1+2)
|
val |= (cond_bit_code << 12+4 +4+1+1+1+1 +1+1+2)
|
||||||
io.write_uint32 val
|
io.write_uint32 val
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -29,8 +29,9 @@ module Ast
|
|||||||
end
|
end
|
||||||
|
|
||||||
def compile context
|
def compile context
|
||||||
# TODO check if needst to be added?
|
value = Vm::StringLiteral.new(string)
|
||||||
Vm::ObjectReference.new( Vm::StringValue.new(string) )
|
context.program.add_object value
|
||||||
|
value
|
||||||
end
|
end
|
||||||
def == other
|
def == other
|
||||||
compare other , [:string]
|
compare other , [:string]
|
||||||
|
@ -12,8 +12,8 @@ module Ast
|
|||||||
fun = Vm::FunctionCall.new( name , args.collect{ |a| a.compile(context) } )
|
fun = Vm::FunctionCall.new( name , args.collect{ |a| a.compile(context) } )
|
||||||
fun.assign_function context
|
fun.assign_function context
|
||||||
fun.load_args
|
fun.load_args
|
||||||
#puts "funcall #{self.inspect}"
|
|
||||||
fun.do_call
|
fun.do_call
|
||||||
|
fun
|
||||||
end
|
end
|
||||||
|
|
||||||
def == other
|
def == other
|
||||||
|
@ -2,7 +2,7 @@ module Core
|
|||||||
class Kernel
|
class Kernel
|
||||||
|
|
||||||
#there are no Kernel instances, only class methods.
|
#there are no Kernel instances, only class methods.
|
||||||
# We use this module syntax to avoid the (ugly) self.
|
# We use this module syntax to avoid the (ugly) self (also eases searching).
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
def main_start
|
def main_start
|
||||||
#TODO extract args into array of strings
|
#TODO extract args into array of strings
|
||||||
@ -18,9 +18,9 @@ module Core
|
|||||||
def function_exit f_name
|
def function_exit f_name
|
||||||
Vm::Machine.instance.function_exit f_name
|
Vm::Machine.instance.function_exit f_name
|
||||||
end
|
end
|
||||||
def self.puts string
|
def putstring
|
||||||
# should unwrap from string to char*
|
# should unwrap from string to char*
|
||||||
Vm::Machine.instance.puts string
|
Vm::Machine.instance.putstring
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -56,12 +56,12 @@ module Vm
|
|||||||
obj.assemble io
|
obj.assemble io
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# set the next executed block after self.
|
# set the next executed block after self.
|
||||||
# why is this useful? if it's unconditional, why not merge them:
|
# why is this useful? if it's unconditional, why not merge them:
|
||||||
# So the second block can be used as a jump target. You standard loop needs a block to setup
|
# So the second block can be used as a jump target. You standard loop needs a block to setup
|
||||||
# and at least one to do the calculation
|
# and at least one to do the calculation
|
||||||
def next block
|
def set_next block
|
||||||
@next = block
|
@next = block
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ module Vm
|
|||||||
# in other words, during assembly the position _must_ be resolved into a pc relative address
|
# in other words, during assembly the position _must_ be resolved into a pc relative address
|
||||||
# and not used as is
|
# and not used as is
|
||||||
def position
|
def position
|
||||||
throw "Not set" unless @position
|
|
||||||
@position
|
@position
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -6,49 +6,67 @@ module Vm
|
|||||||
|
|
||||||
# Functions also have arguments, though they are handled differently (in register allocation)
|
# Functions also have arguments, though they are handled differently (in register allocation)
|
||||||
|
|
||||||
# Functions have a minimum of two blocks, entry and exit, which are created for you
|
# Functions have a exactly three blocks, entry, exit and body, which are created for you
|
||||||
# but there is no branch created between them, this must be done by the programmer.
|
# with straight branches between them.
|
||||||
|
|
||||||
|
|
||||||
class Function < Block
|
# Also remember that if your den body exists of severa blocks, they must be wrapped in a
|
||||||
|
# block as the function really only has the one, and blocks only assemble their codes,
|
||||||
|
# not their next links
|
||||||
|
# This comes at zero runtime cost though, as the wrapper is just the sum of it's codes
|
||||||
|
|
||||||
|
# If you change the body block to point elsewhere, remember to end up at exit
|
||||||
|
|
||||||
|
class Function < Code
|
||||||
|
|
||||||
def initialize(name , args = [])
|
def initialize(name , args = [])
|
||||||
super(name)
|
super()
|
||||||
|
@name = name
|
||||||
@args = args
|
@args = args
|
||||||
@entry = Core::Kernel::function_entry( name )
|
@entry = Core::Kernel::function_entry( name )
|
||||||
@exit = Core::Kernel::function_exit( name )
|
@exit = Core::Kernel::function_exit( name )
|
||||||
|
@body = Block.new("#{name}_body")
|
||||||
|
branch_body
|
||||||
end
|
end
|
||||||
attr_reader :args , :entry , :exit
|
attr_reader :args , :entry , :exit , :body , :name
|
||||||
|
|
||||||
|
# this creates a branch from entry here and from here to exit
|
||||||
|
# unless there is a link existing, in which you are resposible
|
||||||
|
def set_body body
|
||||||
|
@body = body
|
||||||
|
branch_body
|
||||||
|
end
|
||||||
|
|
||||||
def arity
|
def arity
|
||||||
@args.length
|
@args.length
|
||||||
end
|
end
|
||||||
|
|
||||||
def link_at address , context
|
def link_at address , context
|
||||||
# function = context.program.get_function(name)
|
raise "undefined code #{inspect}" if @body.nil?
|
||||||
# unless function
|
super #just sets the position
|
||||||
# function = Core::Kernel.send(name)
|
|
||||||
# context.program.get_or_create_function( name , function , arity )
|
|
||||||
# end
|
|
||||||
|
|
||||||
@entry.link_at address , context
|
@entry.link_at address , context
|
||||||
address += @entry.length
|
address += @entry.length
|
||||||
super(address , context)
|
@body.link_at(address , context)
|
||||||
address += @entry.length
|
address += @entry.length
|
||||||
@exit.link_at(address,context)
|
@exit.link_at(address,context)
|
||||||
end
|
end
|
||||||
|
|
||||||
def length
|
def length
|
||||||
@entry.length + @exit.length + super
|
@entry.length + @exit.length + @body.length
|
||||||
end
|
end
|
||||||
|
|
||||||
def assemble io
|
def assemble io
|
||||||
@entry.assemble io
|
@entry.assemble(io)
|
||||||
super(io)
|
@body.assemble(io)
|
||||||
@exit.assemble(io)
|
@exit.assemble(io)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
# set up the braches from entry to body and body to exit (unless that exists, see set_body)
|
||||||
|
def branch_body
|
||||||
|
@entry.set_next(@body)
|
||||||
|
@body.set_next(@exit) if @body and !@body.next
|
||||||
|
end
|
||||||
|
|
||||||
def add_arg value
|
def add_arg value
|
||||||
# TODO check
|
# TODO check
|
||||||
@args << value
|
@args << value
|
||||||
|
@ -21,11 +21,12 @@ module Vm
|
|||||||
end
|
end
|
||||||
def load_args
|
def load_args
|
||||||
args.each_with_index do |arg , index|
|
args.each_with_index do |arg , index|
|
||||||
arg.load index
|
add_code arg.load(index)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def do_call
|
def do_call
|
||||||
Machine.instance.function_call self
|
add_code Machine.instance.function_call self
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -49,6 +49,8 @@ module Vm
|
|||||||
fun = get_function name
|
fun = get_function name
|
||||||
unless fun
|
unless fun
|
||||||
fun = Function.new(name)
|
fun = Function.new(name)
|
||||||
|
block = Core::Kernel.send(name)
|
||||||
|
fun.set_body block
|
||||||
@functions << fun
|
@functions << fun
|
||||||
end
|
end
|
||||||
fun
|
fun
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
require "vm/code"
|
|
||||||
|
|
||||||
module Vm
|
|
||||||
# The name really says it all.
|
|
||||||
# The only interesting thing is storage.
|
|
||||||
# Currently string are stored "inline" , ie in the code segment.
|
|
||||||
# Mainly because that works an i aint no elf expert.
|
|
||||||
|
|
||||||
class StringLiteral < Vm::Code
|
|
||||||
|
|
||||||
# currently aligned to 4 (ie padded with 0) and off course 0 at the end
|
|
||||||
def initialize(str)
|
|
||||||
length = str.length
|
|
||||||
# rounding up to the next 4 (always adding one for zero pad)
|
|
||||||
pad = ((length / 4 ) + 1 ) * 4 - length
|
|
||||||
raise "#{pad} #{self}" unless pad >= 1
|
|
||||||
@string = str + "\x00" * pad
|
|
||||||
end
|
|
||||||
|
|
||||||
# the strings length plus padding
|
|
||||||
def length
|
|
||||||
@string.length
|
|
||||||
end
|
|
||||||
|
|
||||||
# just writing the string
|
|
||||||
def assemble(io)
|
|
||||||
io << @string
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -41,40 +41,36 @@ module Vm
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Float < Word
|
# The name really says it all.
|
||||||
end
|
# The only interesting thing is storage.
|
||||||
|
# Currently string are stored "inline" , ie in the code segment.
|
||||||
|
# Mainly because that works an i aint no elf expert.
|
||||||
|
|
||||||
class Reference < Word
|
class StringLiteral < Value
|
||||||
end
|
|
||||||
class StringValue < Value
|
# currently aligned to 4 (ie padded with 0) and off course 0 at the end
|
||||||
def initialize string
|
def initialize(str)
|
||||||
@string = string
|
super()
|
||||||
end
|
length = str.length
|
||||||
def length
|
# rounding up to the next 4 (always adding one for zero pad)
|
||||||
@string.length + 3
|
pad = ((length / 4 ) + 1 ) * 4 - length
|
||||||
|
raise "#{pad} #{self}" unless pad >= 1
|
||||||
|
@string = str + "\x00" * pad
|
||||||
end
|
end
|
||||||
attr_reader :string
|
attr_reader :string
|
||||||
end
|
|
||||||
|
|
||||||
class MemoryReference < Reference
|
|
||||||
end
|
|
||||||
|
|
||||||
class ObjectReference < Reference
|
def load reg_num
|
||||||
def initialize obj
|
Machine.instance.string_load self , reg_num
|
||||||
@object = obj
|
end
|
||||||
|
|
||||||
|
# the strings length plus padding
|
||||||
|
def length
|
||||||
|
@string.length
|
||||||
end
|
end
|
||||||
attr_reader :object
|
|
||||||
|
|
||||||
def compiled context
|
# just writing the string
|
||||||
if object.is_a? StringValue
|
def assemble(io)
|
||||||
context.program.add_object object
|
io << @string
|
||||||
else
|
|
||||||
#TODO define object layout more generally and let objects lay themselves out
|
|
||||||
# as it is the program does this (in the objectwriter/stringtable)
|
|
||||||
un.done
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
require_relative "string_literal"
|
|
@ -1 +1 @@
|
|||||||
puts( "hello world" )
|
putstring( "Hello Raisa, I am crystksdfkljsncjncn" )
|
||||||
|
Loading…
x
Reference in New Issue
Block a user