mirror of
https://github.com/Ed94/perfaware.git
synced 2024-12-21 22:44:46 -08:00
wip, the urge to finish the lookup table is real...
This commit is contained in:
parent
356d8076a5
commit
3c712f8be6
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -3,6 +3,7 @@
|
||||
"*.rmd": "markdown",
|
||||
"zpl.h": "c",
|
||||
"cstddef": "cpp",
|
||||
"xtr1common": "cpp"
|
||||
"xtr1common": "cpp",
|
||||
"*.inline": "cpp"
|
||||
}
|
||||
}
|
51
8086/Op.hpp
Normal file
51
8086/Op.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include "bloat.hpp"
|
||||
|
||||
namespace Op
|
||||
{
|
||||
enum Code : u8
|
||||
{
|
||||
#define D_Entry( Entry_ ) Entry,
|
||||
#include "opcodes.inline"
|
||||
Num,
|
||||
#undef D_Entry
|
||||
};
|
||||
|
||||
char const* str( Code code )
|
||||
{
|
||||
static
|
||||
char const* code_to_str[ Num] =
|
||||
{
|
||||
#define D_Entry( Entry_ ) ZPL_STRINGIFY( Entry_ ),
|
||||
#include "opcodes.inline"
|
||||
#undef D_Entry
|
||||
};
|
||||
|
||||
return code_to_str[ code ];
|
||||
}
|
||||
|
||||
char const* meumonic( Code code )
|
||||
{
|
||||
static
|
||||
char const* code_to_str[ Num] =
|
||||
{
|
||||
#define D_Entry( Entry_ ) ZPL_STRINGIFY( Entry_ ),
|
||||
#include "opcodes.meumonics.inline"
|
||||
#undef D_Entry
|
||||
};
|
||||
|
||||
return code_to_str[ code ];
|
||||
}
|
||||
|
||||
char const* intuitive( Code code )
|
||||
{
|
||||
static
|
||||
char const* code_to_str[ Num] =
|
||||
{
|
||||
#define D_Entry( Entry_ ) ZPL_STRINGIFY( Entry_ ),
|
||||
#include "opcodes.intuitive.inline"
|
||||
#undef D_Entry
|
||||
};
|
||||
|
||||
return code_to_str[ Num];
|
||||
}
|
||||
}
|
146
8086/opcodes.inline
Normal file
146
8086/opcodes.inline
Normal file
@ -0,0 +1,146 @@
|
||||
D_Entry( add_00 )
|
||||
D_Entry( add_01 )
|
||||
D_Entry( add_02 )
|
||||
D_Entry( add_04 )
|
||||
D_Entry( add_05 )
|
||||
D_Entry( push_06 )
|
||||
D_Entry( pop_07 )
|
||||
D_Entry( or_10 )
|
||||
D_Entry( or_11 )
|
||||
D_Entry( or_12 )
|
||||
D_Entry( or_13 )
|
||||
D_Entry( or_14 )
|
||||
D_Entry( or_15 )
|
||||
D_Entry( push_16 )
|
||||
D_Entry( _17 )
|
||||
D_Entry( adc_20 )
|
||||
D_Entry( adc_21 )
|
||||
D_Entry( adc_22 )
|
||||
D_Entry( adc_23 )
|
||||
D_Entry( adc_24 )
|
||||
D_Entry( adc_25 )
|
||||
D_Entry( push_26 )
|
||||
D_Entry( pop_27 )
|
||||
D_Entry( sbb_30 )
|
||||
D_Entry( sbb_31 )
|
||||
D_Entry( sbb_32 )
|
||||
D_Entry( sbb_33 )
|
||||
D_Entry( sbb_34 )
|
||||
D_Entry( sbb_35 )
|
||||
D_Entry( push_36 )
|
||||
D_Entry( pop_37 )
|
||||
D_Entry( and_40 )
|
||||
D_Entry( and_41 )
|
||||
D_Entry( and_42 )
|
||||
D_Entry( and_43 )
|
||||
D_Entry( and_44 )
|
||||
D_Entry( and_45 )
|
||||
D_Entry( es_46 )
|
||||
D_Entry( daa_47 )
|
||||
D_Entry( sub_50 )
|
||||
D_Entry( sub_51 )
|
||||
D_Entry( sub_52 )
|
||||
D_Entry( sub_53 )
|
||||
D_Entry( sub_54 )
|
||||
D_Entry( sub_55 )
|
||||
D_Entry( cs_56 )
|
||||
D_Entry( das_57 )
|
||||
D_Entry( xor_60 )
|
||||
D_Entry( xor_61 )
|
||||
D_Entry( xor_62 )
|
||||
D_Entry( xor_63 )
|
||||
D_Entry( xor_64 )
|
||||
D_Entry( xor_65 )
|
||||
D_Entry( ss_66 )
|
||||
D_Entry( aaa_67 )
|
||||
D_Entry( cmp_70 )
|
||||
D_Entry( cmp_71 )
|
||||
D_Entry( cmp_72 )
|
||||
D_Entry( cmp_73 )
|
||||
D_Entry( cmp_74 )
|
||||
D_Entry( cmp_75 )
|
||||
D_Entry( ds_75 )
|
||||
D_Entry( ds_76 )
|
||||
D_Entry( aas_107 )
|
||||
D_Entry( inc_100 )
|
||||
D_Entry( inc_101 )
|
||||
D_Entry( inc_102 )
|
||||
D_Entry( inc_103 )
|
||||
D_Entry( inc_104 )
|
||||
D_Entry( inc_105 )
|
||||
D_Entry( inc_106 )
|
||||
D_Entry( inc_107 )
|
||||
D_Entry( dec_100 )
|
||||
D_Entry( dec_101 )
|
||||
D_Entry( dec_102 )
|
||||
D_Entry( dec_103 )
|
||||
D_Entry( dec_104 )
|
||||
D_Entry( dec_105 )
|
||||
D_Entry( dec_106 )
|
||||
D_Entry( dec_107 )
|
||||
D_Entry( push_110 )
|
||||
D_Entry( push_111 )
|
||||
D_Entry( push_112 )
|
||||
D_Entry( push_113 )
|
||||
D_Entry( push_114 )
|
||||
D_Entry( push_115 )
|
||||
D_Entry( push_116 )
|
||||
D_Entry( push_117 )
|
||||
D_Entry( pop_120 )
|
||||
D_Entry( pop_121 )
|
||||
D_Entry( pop_122 )
|
||||
D_Entry( pop_123 )
|
||||
D_Entry( pop_124 )
|
||||
D_Entry( pop_125 )
|
||||
D_Entry( pop_126 )
|
||||
D_Entry( pop_127 )
|
||||
D_Entry( pop_130 )
|
||||
D_Entry( pop_131 )
|
||||
D_Entry( pop_132 )
|
||||
D_Entry( pop_133 )
|
||||
D_Entry( pop_134 )
|
||||
D_Entry( pop_135 )
|
||||
D_Entry( pop_136 )
|
||||
D_Entry( pop_137 )
|
||||
D_Entry( _140 )
|
||||
D_Entry( _141 )
|
||||
D_Entry( _142 )
|
||||
D_Entry( _143 )
|
||||
D_Entry( _144 )
|
||||
D_Entry( _145 )
|
||||
D_Entry( _146 )
|
||||
D_Entry( _147 )
|
||||
D_Entry( _150 )
|
||||
D_Entry( _151 )
|
||||
D_Entry( _152 )
|
||||
D_Entry( _153 )
|
||||
D_Entry( _154 )
|
||||
D_Entry( _155 )
|
||||
D_Entry( _156 )
|
||||
D_Entry( _157 )
|
||||
D_Entry( jo_160 )
|
||||
D_Entry( jno_161 )
|
||||
D_Entry( jb_jnae_jc_162 )
|
||||
D_Entry( jnb_jae_jnc_163 )
|
||||
D_Entry( je_jz_164 )
|
||||
D_Entry( jne_jnz_165 )
|
||||
D_Entry( jbe_jna_166 )
|
||||
D_Entry( jnbe_ja_167 )
|
||||
D_Entry( js_170 )
|
||||
D_Entry( jns_171 )
|
||||
D_Entry( jp_jpe_172 )
|
||||
D_Entry( jnp_jpo_173 )
|
||||
D_Entry( jl_jnge_174 )
|
||||
D_Entry( jnl_jge_175 )
|
||||
D_Entry( jle_jng_176 )
|
||||
D_Entry( jnle_jg_177 )
|
||||
D_Entry( add_200 )
|
||||
D_Entry( or_201 )
|
||||
D_Entry( adc_201 )
|
||||
D_Entry( sbb_202 )
|
||||
D_Entry( sub_203 )
|
||||
D_Entry( cmp_204 )
|
||||
D_Entry( add_205 )
|
||||
D_Entry( adc_206 )
|
||||
D_Entry( sbb_207 )
|
||||
D_Entry( sub_210 )
|
0
8086/opcodes.intuitive.inline
Normal file
0
8086/opcodes.intuitive.inline
Normal file
0
8086/opcodes.meumonics.inline
Normal file
0
8086/opcodes.meumonics.inline
Normal file
103
part_1/bloat.hpp
103
part_1/bloat.hpp
@ -30,22 +30,101 @@
|
||||
// # define ZPL_MODULE_THREADING
|
||||
// # define ZPL_MODULE_JOBS
|
||||
// # define ZPL_MODULE_PARSER
|
||||
extern "C" {
|
||||
// extern "C" {
|
||||
#include "zpl.h"
|
||||
}
|
||||
// }
|
||||
|
||||
#if __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#undef cast
|
||||
|
||||
#pragma endregion ZPL INCLUDE
|
||||
|
||||
|
||||
|
||||
zpl_arena Global_Arena {};
|
||||
#define g_allocator zpl_arena_allocator( & Global_Arena)
|
||||
|
||||
void setup()
|
||||
{
|
||||
zpl_arena_init_from_allocator( & Global_Arena, zpl_heap(), zpl_megabytes(10) );
|
||||
|
||||
if ( Global_Arena.total_size == 0 )
|
||||
{
|
||||
zpl_assert_crash( "Failed to reserve memory for Tests:: Global_Arena" );
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
zpl_arena_free( & Global_Arena);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma region ZPL Expose
|
||||
zpl_string string_make(zpl_isize capacity)
|
||||
{
|
||||
zpl_isize header_size = zpl_size_of(zpl_string_header);
|
||||
void *ptr = zpl_alloc(g_allocator, header_size + capacity + 1);
|
||||
|
||||
zpl_string str;
|
||||
zpl_string_header *header;
|
||||
|
||||
if (ptr == NULL) return NULL;
|
||||
zpl_zero_size(ptr, header_size + capacity + 1);
|
||||
|
||||
str = cast(char *) ptr + header_size;
|
||||
header = ZPL_STRING_HEADER(str);
|
||||
header->allocator = g_allocator;
|
||||
header->length = 0;
|
||||
header->capacity = capacity;
|
||||
str[capacity] = '\0';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
inline
|
||||
zpl_string string_make(const char *str)
|
||||
{
|
||||
zpl_isize len = str ? zpl_strlen(str) : 0;
|
||||
return zpl_string_make_length(g_allocator, str, len);
|
||||
}
|
||||
|
||||
inline
|
||||
zpl_string string_append(zpl_string str, zpl_string const other)
|
||||
{
|
||||
return zpl_string_append_length(str, other, zpl_string_length(other));
|
||||
}
|
||||
|
||||
zpl_string string_format( char const* fmt, ... )
|
||||
{
|
||||
zpl_local_persist zpl_thread_local char buf[ZPL_PRINTF_MAXLEN] = { 0 };
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
zpl_snprintf_va(buf, ZPL_PRINTF_MAXLEN, fmt, va);
|
||||
va_end(va);
|
||||
|
||||
return zpl_string_make(g_allocator, buf);
|
||||
}
|
||||
|
||||
zpl_string string_format( char* buffer, zpl_isize size, char const* fmt, ... )
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
zpl_snprintf_va(buffer, size, fmt, va);
|
||||
va_end(va);
|
||||
|
||||
return zpl_string_make(g_allocator, buffer);
|
||||
}
|
||||
#pragma endregion ZPL Expose
|
||||
|
||||
|
||||
|
||||
#define bit( Value_ ) ( 1 << Value_ )
|
||||
#define bitfield_is_equal( Field_, Mask_ ) ( ( Mask_ & Field_ ) == Mask_ )
|
||||
#undef cast
|
||||
#define cast( Type_ , Value_ ) ( ( Type_ )( Value_ ) )
|
||||
#define ct constexpr
|
||||
#define gen( ... ) template< __VA_ARGS__ >
|
||||
@ -54,13 +133,31 @@ extern "C" {
|
||||
#define Msg_Invalid_Value "INVALID VALUE PROVIDED"
|
||||
#define scast static_cast
|
||||
#define rcast reinterpret_cast
|
||||
#define pcast( Type_, Value_ ) ( * (Type_*)( & Value_ ) )
|
||||
|
||||
|
||||
|
||||
using s8 = zpl_i8;
|
||||
using u8 = zpl_u8;
|
||||
using u16 = zpl_u16;
|
||||
using u32 = zpl_u32;
|
||||
using f64 = zpl_f64;
|
||||
using uw = zpl_usize;
|
||||
using sw = zpl_isize;
|
||||
|
||||
struct u16_Split
|
||||
{
|
||||
u8 Low;
|
||||
u8 High;
|
||||
|
||||
operator u16()
|
||||
{
|
||||
return * cast( u16*, this );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
ct inline
|
||||
char char_binary( u8 value, u8 pos )
|
||||
{
|
||||
|
@ -15,5 +15,15 @@ if not exist tests\listing_0038_many_register_mov (
|
||||
call nasm ".\tests\listing_0038_many_register_mov.asm"
|
||||
)
|
||||
|
||||
if not exist tests\listing_0039_more_movs (
|
||||
echo Assembling: listing_0039_more_movs.asm
|
||||
call nasm ".\tests\listing_0039_more_movs.asm"
|
||||
)
|
||||
|
||||
if not exist tests\listing_0040_challenge_movs (
|
||||
echo Assembling: listing_0040_challenge_movs.asm
|
||||
call nasm ".\tests\listing_0040_challenge_movs.asm"
|
||||
)
|
||||
|
||||
echo:
|
||||
ninja -C build
|
@ -11,3 +11,12 @@ if exist tests\listing_0037_single_register_mov (
|
||||
if exist tests\listing_0038_many_register_mov (
|
||||
DEL /Q tests\listing_0038_many_register_mov
|
||||
)
|
||||
|
||||
if exist tests\listing_0039_more_movs (
|
||||
DEL /Q tests\listing_0039_more_movs
|
||||
)
|
||||
|
||||
if exist tests\listing_0040_challenge_movs (
|
||||
DEL /Q tests\listing_0040_challenge_movs
|
||||
)
|
||||
|
||||
|
@ -1,60 +1,46 @@
|
||||
#include "bloat.hpp"
|
||||
|
||||
zpl_arena BlobArena {};
|
||||
#define allocator zpl_arena_allocator( & BlobArena)
|
||||
|
||||
void setup()
|
||||
{
|
||||
zpl_arena_init_from_allocator( & BlobArena, zpl_heap(), zpl_megabytes(10) );
|
||||
|
||||
if ( BlobArena.total_size == 0 )
|
||||
{
|
||||
zpl_assert_crash( "Failed to reserve memory for Tests:: BLobArena" );
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
zpl_arena_free( & BlobArena);
|
||||
}
|
||||
|
||||
// Binary formatting for literals is used heavily as that its
|
||||
// how the encoding is conveyed in the hardware reference from intel.
|
||||
// Octal formatting is used heavily throughout
|
||||
// See: https://gist.github.com/seanjensengrey/f971c20d05d4d0efc0781f2f3c0353da
|
||||
// (x86 is an octal machine)
|
||||
|
||||
namespace Op
|
||||
{
|
||||
ct u8 Mask = 00210;
|
||||
ct u8 Mask_Low = 0b11110000;
|
||||
ct u8 Mask_High = 0b00001111;
|
||||
ct u8 Mask_Imme = 0b00111000;
|
||||
|
||||
#define D_Opcodes \
|
||||
D_Entry( mov_88 ) \
|
||||
D_Entry( mov_89 ) \
|
||||
D_Entry( mov_RM_R ) \
|
||||
D_Entry( mov_Im_RM ) \
|
||||
D_Entry( mov_Im_R ) \
|
||||
D_Entry( mov_M_Acc ) \
|
||||
D_Entry( mov_Acc_M ) \
|
||||
D_Entry( mov_RM_SR ) \
|
||||
D_Entry( mov_SR_RM ) \
|
||||
|
||||
enum Code : u8
|
||||
{
|
||||
Mask = 0b11111100,
|
||||
mov_88 = 0b10001000,
|
||||
mov_89 = 0b11000100,
|
||||
mov_RM_R = 0b10001000,
|
||||
mov_Im_RM = 0b11001100,
|
||||
mov_Im_R = 0b10110000,
|
||||
mov_M_Acc = 0b10100000,
|
||||
mov_Acc_M = 0b10100010,
|
||||
mov_RM_SR = 0b10001110,
|
||||
mov_SR_RM = 0b10001100
|
||||
};
|
||||
|
||||
char const* str( Code type )
|
||||
{
|
||||
switch ( type )
|
||||
{
|
||||
case mov_88:
|
||||
return "mov_88";
|
||||
case mov_89:
|
||||
return "mov_89";
|
||||
}
|
||||
#define D_Entry( Entry_ ) \
|
||||
case Entry_: \
|
||||
return ZPL_STRINGIFY(Entry_); \
|
||||
|
||||
return Msg_Invalid_Value;
|
||||
}
|
||||
|
||||
ct char const* helper_meumonic( Code type )
|
||||
{
|
||||
switch ( type )
|
||||
{
|
||||
case mov_88:
|
||||
case mov_89:
|
||||
return "mov";
|
||||
D_Opcodes
|
||||
#undef D_Entry
|
||||
}
|
||||
|
||||
return Msg_Invalid_Value;
|
||||
@ -64,32 +50,30 @@ namespace Op
|
||||
{
|
||||
switch ( type )
|
||||
{
|
||||
case mov_88:
|
||||
case mov_89:
|
||||
case mov_RM_R :
|
||||
case mov_Im_RM :
|
||||
case mov_Im_R :
|
||||
case mov_M_Acc :
|
||||
case mov_Acc_M :
|
||||
case mov_RM_SR :
|
||||
case mov_SR_RM :
|
||||
return "mov";
|
||||
}
|
||||
|
||||
return Msg_Invalid_Value;
|
||||
}
|
||||
|
||||
ct char const* helper_intuitive( Code type )
|
||||
{
|
||||
switch ( type )
|
||||
{
|
||||
case mov_88:
|
||||
case mov_89:
|
||||
return "move";
|
||||
}
|
||||
|
||||
return Msg_Invalid_Value;
|
||||
}
|
||||
|
||||
char const* intuitive( Code type )
|
||||
{
|
||||
switch ( type )
|
||||
{
|
||||
case mov_88:
|
||||
case mov_89:
|
||||
case mov_RM_R :
|
||||
case mov_Im_RM :
|
||||
case mov_Im_R :
|
||||
case mov_M_Acc :
|
||||
case mov_Acc_M :
|
||||
case mov_RM_SR :
|
||||
case mov_SR_RM :
|
||||
return "move";
|
||||
}
|
||||
|
||||
@ -101,9 +85,8 @@ namespace Op
|
||||
|
||||
namespace Field
|
||||
{
|
||||
// Effective Address offset
|
||||
inline
|
||||
u8 effective_address( u8 reg )
|
||||
u8 offset_left_reg( u8 reg )
|
||||
{
|
||||
return reg << 3;
|
||||
}
|
||||
@ -135,6 +118,7 @@ namespace Field
|
||||
enum Width : u8
|
||||
{
|
||||
Mask_Width = 0b00000001,
|
||||
Mask_Width_Imme = 0b00001000,
|
||||
Width_Byte = 0b00,
|
||||
Width_Word = 0b01,
|
||||
};
|
||||
@ -184,17 +168,61 @@ namespace Field
|
||||
return Msg_Invalid_Value;
|
||||
}
|
||||
|
||||
#if 0
|
||||
ct u8 RegMem_AL = 0b00000000;
|
||||
ct u8 RegMem_AX = 0b00000000;
|
||||
ct u8 RegMem_BX_SI = 0b00000000;
|
||||
ct u8 RegMem_BX_SI_D8 = 0b00000000;
|
||||
ct u8 RegMem_BX_SI_D16 = 0b00000000;
|
||||
#endif
|
||||
ct u8 Mask_Memory = 0b00000111;
|
||||
|
||||
// https://i.imgur.com/Tm5roJu.png
|
||||
enum Memory : u8
|
||||
{
|
||||
Add_BX_SI = 0b000,
|
||||
Add_BX_DI = 0b001,
|
||||
Add_BP_SI = 0b010,
|
||||
Add_BP_DI = 0b011,
|
||||
Add_SI = 0b100,
|
||||
Add_DI = 0b101,
|
||||
Add_Direct = 0b110,
|
||||
Add_BX = 0b111,
|
||||
|
||||
Num_Memory
|
||||
};
|
||||
|
||||
char const* str_memory( Memory mem )
|
||||
{
|
||||
char const* mem_to_str[Num_Memory] =
|
||||
{
|
||||
"BX + SI",
|
||||
"BX + DI",
|
||||
"BP + SI",
|
||||
"BP + DI",
|
||||
"SI",
|
||||
"DI",
|
||||
"BP",
|
||||
"BX"
|
||||
};
|
||||
|
||||
return mem_to_str[ mem ];
|
||||
}
|
||||
|
||||
char const* str_memory_intuitive( Memory mem )
|
||||
{
|
||||
char const* mem_to_str[Num_Memory] =
|
||||
{
|
||||
"Base.16 + Stack.Index",
|
||||
"Base.16 + Destination.Index",
|
||||
"Stack.Base + Stack.Index",
|
||||
"Stack.Base + Destination.Index",
|
||||
"Stack.Index",
|
||||
"Destination.Index",
|
||||
"Stack.Base",
|
||||
"BX"
|
||||
};
|
||||
|
||||
return mem_to_str[ mem ];
|
||||
}
|
||||
}
|
||||
|
||||
namespace Register
|
||||
{
|
||||
ct u8 Mask_Imme = 0b00000111;
|
||||
ct u8 Mask_Left = 0b00111000;
|
||||
ct u8 Mask_Right = 0b00000111;
|
||||
|
||||
@ -251,7 +279,7 @@ namespace Register
|
||||
#undef D_Entry
|
||||
};
|
||||
|
||||
return Type_To_Meumonic[ type + Width * 7 + 1 ];
|
||||
return Type_To_Meumonic[ type + Width * ( 7 + 1 ) ];
|
||||
}
|
||||
|
||||
char const* intuitive( Type type, u8 Width )
|
||||
@ -259,38 +287,77 @@ namespace Register
|
||||
static char const*
|
||||
Type_To_Intuitive[ Num ] =
|
||||
{
|
||||
"A.Low",
|
||||
"C.Low",
|
||||
"D.Low",
|
||||
"B.Low",
|
||||
"A.High",
|
||||
"C.High",
|
||||
"D.High",
|
||||
"B.High",
|
||||
"A.16",
|
||||
"C.16",
|
||||
"D.16",
|
||||
"B.16",
|
||||
"Accumulator.Low",
|
||||
"Count.Low",
|
||||
"Data.Low",
|
||||
"Base.Low",
|
||||
"Accumulator.High",
|
||||
"Count.High",
|
||||
"Data.High",
|
||||
"Base.High",
|
||||
"Accumulator.16",
|
||||
"Count.16",
|
||||
"Data.16",
|
||||
"Base.16",
|
||||
"Stack.Pointer",
|
||||
"Stack.Base",
|
||||
"Source.Index",
|
||||
"Destination.Index",
|
||||
};
|
||||
|
||||
return Type_To_Intuitive[ type + Width * 7 + 1 ];
|
||||
return Type_To_Intuitive[ type + Width * ( 7 + 1 ) ];
|
||||
}
|
||||
}
|
||||
|
||||
// 8086 Instructions are 1 to 6 bytes in length.
|
||||
|
||||
struct Octal
|
||||
{
|
||||
u8 Low : 3;
|
||||
u8 High : 3;
|
||||
u8 Dir : 1;
|
||||
u8 Write : 1;
|
||||
|
||||
operator u8()
|
||||
{
|
||||
return * cast( u8*, this);
|
||||
}
|
||||
};
|
||||
|
||||
struct POD_Instruction
|
||||
{
|
||||
u8 Byte_1;
|
||||
u8 Byte_2;
|
||||
u8 Byte_3;
|
||||
u8 Byte_4;
|
||||
u8 Byte_5;
|
||||
u8 Byte_6;
|
||||
// 8086 Instructions are 1 to 6 bytes in length.
|
||||
union
|
||||
{
|
||||
u8* Ptr;
|
||||
u8 Byte[6];
|
||||
|
||||
Octal Instr;
|
||||
|
||||
struct
|
||||
{
|
||||
u8 Pad[2];
|
||||
u16_Split Disp;
|
||||
u16_Split Data;
|
||||
}
|
||||
EffAddr;
|
||||
|
||||
struct
|
||||
{
|
||||
u8 Pad[1];
|
||||
u16_Split Data;
|
||||
}
|
||||
Imme;
|
||||
|
||||
struct
|
||||
{
|
||||
u8 Pad;
|
||||
u16_Split Data;
|
||||
}
|
||||
Addr;
|
||||
};
|
||||
|
||||
// If directly referencing blob data:
|
||||
// Part of next instruction, do not use as stratch memory.
|
||||
u16 Pad;
|
||||
};
|
||||
|
||||
@ -299,120 +366,232 @@ struct Instruction : public POD_Instruction
|
||||
using Code = Op::Code;
|
||||
using Direction = Field::Direction;
|
||||
using Mode = Field::Mode;
|
||||
using Memory = Field::Memory;
|
||||
using Width = Field::Width;
|
||||
using Reg = Register::Type;
|
||||
|
||||
inline
|
||||
Direction direction()
|
||||
Direction get_direction()
|
||||
{
|
||||
Direction
|
||||
direction = cast(Direction, Byte_1 & Field::Mask_Dir);
|
||||
Direction direction = cast(Direction, Byte[0] & Field::Mask_Dir);
|
||||
|
||||
return direction;
|
||||
}
|
||||
|
||||
inline
|
||||
Mode mode()
|
||||
Mode get_mode()
|
||||
{
|
||||
Mode mode = cast( Mode, Byte_2 & Field::Mask_Mode );
|
||||
Mode mode = cast( Mode, Byte[1] & Field::Mask_Mode );
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
inline
|
||||
Code opcode()
|
||||
Code get_opcode( u8 mask )
|
||||
{
|
||||
Op::Code
|
||||
opcode = cast( Op::Code, Byte_1 & Op::Mask );
|
||||
Op::Code opcode = cast( Op::Code, Byte[0] & mask );
|
||||
|
||||
return opcode;
|
||||
}
|
||||
|
||||
inline
|
||||
Reg operand_left()
|
||||
Width get_width( u8 mask )
|
||||
{
|
||||
Width width = cast( Width, Byte[0] & mask );
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
inline
|
||||
Reg mode_operand_left_reg()
|
||||
{
|
||||
u8
|
||||
operand = Byte_2 & Register::Mask_Left;
|
||||
operand = Byte[1] & Register::Mask_Left;
|
||||
operand >>= 3;
|
||||
|
||||
return cast(Reg, operand);
|
||||
}
|
||||
|
||||
inline
|
||||
Reg operand_right()
|
||||
Reg mode_operand_right_reg()
|
||||
{
|
||||
u8 operand = Byte_2 & Register::Mask_Right;
|
||||
u8 operand = Byte[1] & Register::Mask_Right;
|
||||
|
||||
return cast( Reg, operand );
|
||||
}
|
||||
|
||||
inline
|
||||
Width width()
|
||||
{
|
||||
Width width = cast( Width, Byte_1 & Field::Mask_Width );
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
void dissassemble( zpl_string* result_out )
|
||||
forceinline
|
||||
void dissassemble_mode( char const*& str_operand, Width& width, u8& length )
|
||||
{
|
||||
using namespace Field;
|
||||
using namespace Op;
|
||||
using namespace Register;
|
||||
|
||||
Direction dir = direction();
|
||||
Width width_val = width();
|
||||
switch ( get_mode() )
|
||||
{
|
||||
case Mode_Mem:
|
||||
{
|
||||
Memory operand_right = cast( Memory, Byte[1] & Mask_Memory );
|
||||
|
||||
char const* opcode_str = Op::meumonic( opcode() );
|
||||
char const* direction_str = Field::str_direction( dir );
|
||||
char const* width_str = Field::str_width( width_val );
|
||||
char const* mode_str = Field::str_mode( mode() );
|
||||
char const* operand_left_str = Register::meumonic( operand_left(), width_val );
|
||||
char const* operand_right_str = Register::meumonic( operand_right(), width_val );
|
||||
length += 2;
|
||||
|
||||
char
|
||||
binary_string[9];
|
||||
binary_string[8] = '\0';
|
||||
if ( operand_right == Add_Direct )
|
||||
{
|
||||
u16 address = EffAddr.Disp;
|
||||
|
||||
#if Build_Debug
|
||||
str_binary( binary_string, opcode() );
|
||||
zpl_printf("\nOpcode : %s : %s", binary_string, opcode_str);
|
||||
str_operand = string_format( "[ %u ]", address );
|
||||
}
|
||||
else
|
||||
{
|
||||
str_operand = str_memory( operand_right );
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
str_binary( binary_string, dir );
|
||||
zpl_printf("\nDirection : %s : %s", binary_string, direction_str);
|
||||
case Mode_Mem8:
|
||||
{
|
||||
Memory operand_right = cast( Memory, Byte[1] & Mask_Memory );
|
||||
|
||||
str_binary( binary_string, width_val );
|
||||
zpl_printf("\nWidth : %s : %s", binary_string, width_str);
|
||||
length += 1;
|
||||
|
||||
str_binary( binary_string, mode() );
|
||||
zpl_printf("\nMode : %s : %s", binary_string, mode_str);
|
||||
str_operand = string_format( "[ %s + %u ]", str_memory( operand_right), EffAddr.Disp.Low );
|
||||
}
|
||||
break;
|
||||
|
||||
str_binary( binary_string, operand_left() );
|
||||
zpl_printf("\nOperand : %s : %s", binary_string, operand_left_str);
|
||||
case Mode_Mem16:
|
||||
{
|
||||
length += 2;
|
||||
|
||||
str_binary( binary_string, operand_right() );
|
||||
zpl_printf("\nOperand EA : %s : %s", binary_string, operand_right_str);
|
||||
#endif
|
||||
Memory operand_right = cast( Memory, Byte[1] & Mask_Memory );
|
||||
u16 address = EffAddr.Disp;
|
||||
|
||||
if ( result_out == nullptr )
|
||||
return;
|
||||
str_operand = string_format( "[ %s + %u ]", str_memory( operand_right), address );
|
||||
}
|
||||
break;
|
||||
|
||||
if ( * result_out == nullptr )
|
||||
* result_out = zpl_string_make_reserve( allocator, zpl_kilobytes(1) );
|
||||
case Mode_Reg:
|
||||
str_operand = Register::meumonic( mode_operand_right_reg(), width );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
zpl_string assembly = zpl_string_make_reserve( allocator, 32);
|
||||
u8 dissassemble( zpl_string* result_out )
|
||||
{
|
||||
using namespace Field;
|
||||
using namespace Op;
|
||||
using namespace Register;
|
||||
|
||||
assembly = zpl_string_sprintf( allocator, assembly, zpl_kilobytes(1) - 1, "\n%s %s, %s"
|
||||
, opcode_str
|
||||
, dir == Dir_Src ? operand_right_str : operand_left_str
|
||||
, dir == Dir_Src ? operand_left_str : operand_right_str
|
||||
u8 length = 1;
|
||||
|
||||
zpl_string assembly = string_make( 32);
|
||||
|
||||
Code code = get_opcode( Op::Mask );
|
||||
Direction dir = get_direction();
|
||||
|
||||
char const* str_operand_left = nullptr;
|
||||
char const* str_operand_right = nullptr;
|
||||
|
||||
const u8 code_low = Byte[0] & Op::Mask_Low;
|
||||
const u8 code_high = Byte[0] & Op::Mask_High;
|
||||
const u8 sig_imme = Byte[0] & Op::Mask_Imme;
|
||||
|
||||
if ( get_opcode( Op::Mask_Low ) == mov_Im_R )
|
||||
{
|
||||
Width width = get_width( Mask_Width_Imme );
|
||||
Reg reg = cast(Reg, Byte[0] & Register::Mask_Right );
|
||||
u16 immediate = width == Width_Byte ?
|
||||
Imme.Data.Low : Imme.Data;
|
||||
|
||||
str_operand_left = Register::meumonic( reg, width );
|
||||
str_operand_right = string_format( "%u", immediate );
|
||||
}
|
||||
else
|
||||
{
|
||||
switch ( code )
|
||||
{
|
||||
#pragma region mov
|
||||
case mov_RM_R:
|
||||
{
|
||||
length++;
|
||||
|
||||
Width width = get_width( Mask_Width );
|
||||
|
||||
str_operand_left = Register::meumonic( mode_operand_left_reg(), width );
|
||||
|
||||
dissassemble_mode( str_operand_right, width, length );
|
||||
}
|
||||
break;
|
||||
|
||||
case mov_Im_RM:
|
||||
{
|
||||
Width width = get_width( Mask_Width_Imme );
|
||||
|
||||
Memory operand_left = cast( Memory, Byte[1] & Mask_Memory );
|
||||
u16 address = EffAddr.Disp;
|
||||
u16 immediate = width == Width_Byte ?
|
||||
EffAddr.Data.Low : EffAddr.Data;
|
||||
|
||||
dissassemble_mode( str_operand_left, width, length );
|
||||
|
||||
str_operand_right = string_format( "%u", immediate );
|
||||
}
|
||||
break;
|
||||
|
||||
case mov_M_Acc:
|
||||
{
|
||||
Width width = get_width( Mask_Width );
|
||||
u16 address = width == Width_Byte ?
|
||||
Addr.Data.Low : Addr.Data;
|
||||
|
||||
str_operand_left = Register::meumonic( Reg::AX, Width_Word );
|
||||
str_operand_right = string_format( "%u", address );
|
||||
}
|
||||
break;
|
||||
|
||||
case mov_Acc_M:
|
||||
{
|
||||
Width width = get_width( Mask_Width );
|
||||
u16 address = width == Width_Byte ?
|
||||
Addr.Data.Low : Addr.Data;
|
||||
|
||||
str_operand_right = string_format( "%u", address );
|
||||
str_operand_left = Register::meumonic( Reg::AX, Width_Word );
|
||||
}
|
||||
break;
|
||||
|
||||
case mov_RM_SR:
|
||||
{
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case mov_SR_RM:
|
||||
{
|
||||
|
||||
}
|
||||
break;
|
||||
#pragma endregion mov
|
||||
}
|
||||
}
|
||||
|
||||
assembly = string_format( assembly, 32, "\n%s %s, %s"
|
||||
// opcode operand_right, operand_left
|
||||
, Op::meumonic( code)
|
||||
, str_operand_right
|
||||
, str_operand_left
|
||||
);
|
||||
|
||||
* result_out = zpl_string_append( * result_out, assembly );
|
||||
if ( result_out == nullptr )
|
||||
return length;
|
||||
|
||||
if ( * result_out == nullptr )
|
||||
* result_out = string_make( zpl_kilobytes(1) );
|
||||
|
||||
* result_out = string_append( * result_out, assembly );
|
||||
|
||||
#if Build_Debug
|
||||
zpl_printf("\n\nAssembly: %s\n\n", assembly);
|
||||
#endif
|
||||
|
||||
return length;
|
||||
}
|
||||
};
|
||||
|
||||
@ -426,14 +605,14 @@ namespace Tests
|
||||
|
||||
Instruction
|
||||
mock; // mov CX, BX
|
||||
mock.Byte_1 = mov_88 | Dir_Src | Field::Width_Word;
|
||||
mock.Byte_2 = Field::Mode_Reg | effective_address(BX) | CX;
|
||||
mock.Byte[0] = mov_RM_R | Dir_Src | Field::Width_Word;
|
||||
mock.Byte[1] = Field::Mode_Reg | offset_left_reg(BX) | CX;
|
||||
|
||||
zpl_printf("\n\nAttempting Mock Instruction: mov CX, BX");
|
||||
|
||||
print_nl();
|
||||
print_as_binary( & mock.Byte_1, 1, " " );
|
||||
print_as_binary( & mock.Byte_2, 1, " " );
|
||||
print_as_binary( & mock.Byte[0], 1, " " );
|
||||
print_as_binary( & mock.Byte[1], 1, " " );
|
||||
print_nl();
|
||||
|
||||
zpl_string dissasembly = nullptr;
|
||||
@ -441,12 +620,12 @@ namespace Tests
|
||||
mock.dissassemble( & dissasembly);
|
||||
}
|
||||
|
||||
void try_blob_single_instruction()
|
||||
void try_blob_single_move()
|
||||
{
|
||||
zpl_printf("\n\nAttempting to read blob: listing_0037_single_register_mov");
|
||||
|
||||
zpl_file_contents
|
||||
blob = zpl_file_read_contents( allocator, false, "tests/listing_0037_single_register_mov" );
|
||||
blob = zpl_file_read_contents( g_allocator, false, "tests/listing_0037_single_register_mov" );
|
||||
|
||||
if (blob.data == nullptr )
|
||||
return;
|
||||
@ -462,17 +641,17 @@ namespace Tests
|
||||
|
||||
Instruction
|
||||
instr;
|
||||
instr.Byte_1 = data[0];
|
||||
instr.Byte_2 = data[1];
|
||||
instr.Byte[0] = data[0];
|
||||
instr.Byte[1] = data[1];
|
||||
instr.dissassemble( & dissasembly);
|
||||
}
|
||||
|
||||
void try_blob_many_instructions()
|
||||
void try_blob_many_moves()
|
||||
{
|
||||
zpl_printf("\n\nAttempting to read blob: listing_0038_many_register_mov");
|
||||
|
||||
zpl_file_contents
|
||||
blob = zpl_file_read_contents( allocator, false, "tests/listing_0038_many_register_mov" );
|
||||
blob = zpl_file_read_contents( g_allocator, false, "tests/listing_0038_many_register_mov" );
|
||||
|
||||
if (blob.data == nullptr )
|
||||
return;
|
||||
@ -484,14 +663,14 @@ namespace Tests
|
||||
print_as_binary( data, left, " " );
|
||||
print_nl();
|
||||
|
||||
zpl_string dissasembly = zpl_string_make( allocator, "bits 16\n");
|
||||
zpl_string dissasembly = string_make( "bits 16\n");
|
||||
|
||||
while ( left )
|
||||
{
|
||||
Instruction
|
||||
instr;
|
||||
instr.Byte_1 = data[0];
|
||||
instr.Byte_2 = data[1];
|
||||
instr.Byte[0] = data[0];
|
||||
instr.Byte[1] = data[1];
|
||||
instr.dissassemble( & dissasembly);
|
||||
|
||||
data += 2;
|
||||
@ -509,7 +688,47 @@ namespace Tests
|
||||
);
|
||||
}
|
||||
|
||||
#undef allocator
|
||||
void try_blob_more_moves()
|
||||
{
|
||||
zpl_printf("\n\nAttempting to read blob: listing_0039_more_movs");
|
||||
|
||||
zpl_file_contents
|
||||
blob = zpl_file_read_contents( g_allocator, false, "tests/listing_0039_more_movs" );
|
||||
|
||||
if (blob.data == nullptr )
|
||||
return;
|
||||
|
||||
u32 left = blob.size;
|
||||
u8* data = cast( u8*, blob.data );
|
||||
|
||||
print_nl();
|
||||
print_as_binary( data, left, " " );
|
||||
print_nl();
|
||||
|
||||
zpl_string dissasembly = string_make( "bits 16\n");
|
||||
|
||||
while ( left )
|
||||
{
|
||||
Instruction
|
||||
instr;
|
||||
instr.Ptr = data;
|
||||
|
||||
u8 length = instr.dissassemble( & dissasembly);
|
||||
|
||||
data += length;
|
||||
left -= length;
|
||||
}
|
||||
|
||||
zpl_printf("\n\nDissassemlby:\n%s", dissasembly);
|
||||
dissasembly = zpl_string_append_fmt( dissasembly, "\n" );
|
||||
|
||||
zpl_printf("\n\nSaving to file");
|
||||
zpl_file_write_contents("tests/listing_0039_more_movs.asm.out.asm"
|
||||
, dissasembly
|
||||
, zpl_string_length(dissasembly)
|
||||
, nullptr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -519,16 +738,14 @@ int main()
|
||||
|
||||
setup();
|
||||
|
||||
zpl_f64 start = zpl_time_rel();
|
||||
f64 start = zpl_time_rel();
|
||||
|
||||
#if 0
|
||||
Tests::try_mock_instruction();
|
||||
Tests::try_blob_single_instruction();
|
||||
#endif
|
||||
Tests::try_blob_many_instructions();
|
||||
// Tests::try_blob_single_move();
|
||||
// Tests::try_blob_many_moves();
|
||||
Tests::try_blob_more_moves();
|
||||
|
||||
zpl_f64 end = zpl_time_rel();
|
||||
zpl_f64 ms = (end - start) * 100;
|
||||
f64 end = zpl_time_rel();
|
||||
f64 ms = (end - start) * 100;
|
||||
|
||||
printf("\n\nElapsed Time: %lf ms", ms);
|
||||
printf("\n\n");
|
||||
|
@ -1,12 +1,12 @@
|
||||
bits 16
|
||||
|
||||
mov CX, BX
|
||||
mov DH, CH
|
||||
mov CH, AH
|
||||
mov DX, BX
|
||||
mov SI, BX
|
||||
mov BX, DI
|
||||
mov CL, DL
|
||||
mov DH, DH
|
||||
mov AL, CL
|
||||
mov CH, CH
|
||||
mov BX, AX
|
||||
mov BX, SI
|
||||
mov SP, DI
|
||||
|
BIN
part_1/tests/listing_0039_more_movs
Normal file
BIN
part_1/tests/listing_0039_more_movs
Normal file
Binary file not shown.
47
part_1/tests/listing_0039_more_movs.asm
Normal file
47
part_1/tests/listing_0039_more_movs.asm
Normal file
@ -0,0 +1,47 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Please see https://computerenhance.com for further information
|
||||
;
|
||||
; ========================================================================
|
||||
|
||||
; ========================================================================
|
||||
; LISTING 39
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
; Register-to-register
|
||||
mov si, bx
|
||||
mov dh, al
|
||||
|
||||
; 8-bit immediate-to-register
|
||||
mov cl, 12
|
||||
mov ch, -12
|
||||
|
||||
; 16-bit immediate-to-register
|
||||
mov cx, 12
|
||||
mov cx, -12
|
||||
mov dx, 3948
|
||||
mov dx, -3948
|
||||
|
||||
; Source address calculation
|
||||
mov al, [bx + si]
|
||||
mov bx, [bp + di]
|
||||
mov dx, [bp]
|
||||
|
||||
; Source address calculation plus 8-bit displacement
|
||||
mov ah, [bx + si + 4]
|
||||
|
||||
; Source address calculation plus 16-bit displacement
|
||||
mov al, [bx + si + 4999]
|
||||
|
||||
; Dest address calculation
|
||||
mov [bx + di], cx
|
||||
mov [bp + si], cl
|
||||
mov [bp], ch
|
43
part_1/tests/listing_0039_more_movs.asm.out.asm
Normal file
43
part_1/tests/listing_0039_more_movs.asm.out.asm
Normal file
@ -0,0 +1,43 @@
|
||||
bits 16
|
||||
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
||||
INVALID VALUE PROVIDED (null),
|
BIN
part_1/tests/listing_0040_challenge_movs
Normal file
BIN
part_1/tests/listing_0040_challenge_movs
Normal file
Binary file not shown.
38
part_1/tests/listing_0040_challenge_movs.asm
Normal file
38
part_1/tests/listing_0040_challenge_movs.asm
Normal file
@ -0,0 +1,38 @@
|
||||
; ========================================================================
|
||||
;
|
||||
; (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
|
||||
;
|
||||
; This software is provided 'as-is', without any express or implied
|
||||
; warranty. In no event will the authors be held liable for any damages
|
||||
; arising from the use of this software.
|
||||
;
|
||||
; Please see https://computerenhance.com for further information
|
||||
;
|
||||
; ========================================================================
|
||||
|
||||
; ========================================================================
|
||||
; LISTING 40
|
||||
; ========================================================================
|
||||
|
||||
bits 16
|
||||
|
||||
; Signed displacements
|
||||
mov ax, [bx + di - 37]
|
||||
mov [si - 300], cx
|
||||
mov dx, [bx - 32]
|
||||
|
||||
; Explicit sizes
|
||||
mov [bp + di], byte 7
|
||||
mov [di + 901], word 347
|
||||
|
||||
; Direct address
|
||||
mov bp, [5]
|
||||
mov bx, [3458]
|
||||
|
||||
; Memory-to-accumulator test
|
||||
mov ax, [2555]
|
||||
mov ax, [16]
|
||||
|
||||
; Accumulator-to-memory test
|
||||
mov [2554], ax
|
||||
mov [15], ax
|
Loading…
Reference in New Issue
Block a user