All you really need is an instruction set reference, such as:
https://www.felixcloutier.com/x86/index.html
and have a compiler that supports an inline assembler, like the D compiler, with Intel assembler syntax and use it like:
private uint asmBitswap32(uint x) @trusted pure
{
asm pure nothrow @nogc { naked; }
version (D_InlineAsm_X86_64)
{
version (Win64)
asm pure nothrow @nogc { mov EAX, ECX; }
else
asm pure nothrow @nogc { mov EAX, EDI; }
}
asm pure nothrow @nogc
{
// Author: Tiago Gasiba.
mov EDX, EAX;
shr EAX, 1;
and EDX, 0x5555_5555;
and EAX, 0x5555_5555;
shl EDX, 1;
or EAX, EDX;
mov EDX, EAX;
shr EAX, 2;
and EDX, 0x3333_3333;
and EAX, 0x3333_3333;
shl EDX, 2;
or EAX, EDX;
mov EDX, EAX;
shr EAX, 4;
and EDX, 0x0f0f_0f0f;
and EAX, 0x0f0f_0f0f;
shl EDX, 4;
or EAX, EDX;
bswap EAX;
ret;
}
}
The compiler will handle all the program setup and teardown, and you can just concentrate on the assembler part. You can also compile programs with the -vasm switch and the compiler will emit the asm corresponding to the code:
int square(int x) { return x * x; }
compiling:
dmd -c test.d -vasm
prints:
_D4test6squareFiZi:
0000: 0F AF C0 imul EAX,EAX
0003: C3 ret
By trying simple expressions like `x * x` and looking at what the compiler generates, and looking at the instructions in the referenced link, you'll get the hang of it pretty quick.