What can you recommend for someone like me if they wanted to get started with x86?
Ah, if you love disassembly as much as I do, you should check out Ollydbg (linked in the first post). That is a very fun tool to play with, so long as you understand.
Anyway, if you are just looking for basics, then it's pretty much: (Also writing this with those who have not used much ASM in mind, as other people may read this

)
Almost any register can be used for anything.
MOV DEST,SRC - Sets dest to src
Registers (in order): EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI.
AX is the least significant 2 bytes of EAX (the 16-bit equivalents of the registers are just the register names without the "E")
AL is the least significant byte of EAX, and AH is the next one up after that (AH<<8 | AL = AX).
You can use AL, AH, CL, CH, DL, DH, BL, and BH in this manner. ESP, EBP, ESI, and EDI have no equivalent to this.
All arithmetic and bit-manipulation instructions set relevant flags in the flag register.
ADD DEST,SRC - Adds src to dest.
XADD DEST,SRC - Adds src to dest, stores old value of dest in src.
SUB DEST,SRC - Subtracts src from dest.
MUL SRC - Multiplies EAX by src, stores lower 32 bits of result in EAX and higher 32 bits in EDX.
DIV SRC - Divides EAX by src, stores result in EAX and modulus in EDX.
AND DEST,SRC - Performs bitwise AND. (You get the dest/src idea by now

)
OR DEST,SRC - Performs bitwise OR.
XOR DEST,SRC - Performs bitwise XOR.
NOT DEST - Performs bitwise NOT.
SHL DEST,AMT - Shifts dest left by amt bits (C equivalent: dest=dest<<amt; )
SHR DEST,AMT - Shifts dest right by amt bits.
ROL DEST,AMT - Rotates dest left by amt bits. (Like shifting but wraps to the other side)
ROR DEST,AMT - Rotates dest right by amt bits.
XCHG FIRST,SECOND - Swaps 2 values. (E.g. XCHG EAX,EDX. XCHG [mem_ref],AL)
DEC DEST - Subtract 1 from dest (Same as C's dest--; )
INC DEST - Add 1 to dest.
NEG DEST - Set dest to 0-dest.
CMP FIRST,SECOND - Like SUB, but doesn't save the result, only the flags.
TEST FIRST,SECOND - Like CMP, but performs AND.
You can have a maximum of one memory reference per instruction, and it is used like so:
MOV EAX,[mem_ref]
In this case, since we're setting EAX, the size is an implied DWORD. If we were using:
MOVZX EAX,[mem_ref]
(MOVZX being like MOV but extends a smaller data size to 32 bits by setting the unused bytes of those 32 bits to 0). This is invalid, as we could be referencing a WORD or a BYTE.
MOVZX EAX,WORD[mem_ref]
That would be valid. Remember that it does no harm to specify the type of memory you are referencing.
You can of course use it the other way around:
MOV DWORD[mem_ref],EAX
This is also valid - compare:
MOVZX EAX,AL
with:
AND EAX,0xFF
Now, I know I've not structured this well, so you'll have to bear with me.
The registers are named:
EAX = Accumulator (although not strictly - registers are pretty much interchangable)
ECX = Loop Counter (loopd instruction, although that's slower than the "normal" equivalent!)
EDX = Data (Alternative to accumulator in mul/div, etc)
EBX ... ?
ESP = Stack Pointer
EBP = Stack base pointer (frame pointer)
ESI = Source indicator (for string instructions)
EDI = Destination indicator (for string instructions)
Now, assuming knowledge of a stack:
PUSH SRC - (Subtracts 4 from ESP, then) Pushes the value onto the stack as 32-bit.
POP DEST - Pops the value off the stack (, then adds 4 to ESP).
LEAVE - Same effect as MOV ESP,EBP then POP EBP.
ENTER X,Y - Do not use - slower and larger equivalent of normal stack frame setup.
To set up a stack frame (exit it with LEAVE):
PUSH EBP
MOV EBP,ESP
You will see this at the beginning of most functions in programs generated by a compiler.
Now, before we get onto the most basic of instructions, let me pollute your mind with more silly things:
SAR - Like SHR but copies the value of the carry flag instead of zero. (If CF is 1, 00000000 SAR 1 = 10000000)
SAL - Like SHL but copies value of the carry flag instead of zero.
BT VALUE,BIT - Tests the specified bit in the value and stores the state of the bit in the Carry Flag. (BT EAX,0 checks if EAX is even, for example.)
BTC VALUE,BIT - Same as BT but sets the bit to 0 afterwards.
BTS VALUE,BIT - Same as BT but sets the bit to 1 afterwards.
Now, the useful instructions:
JMP value - changes the instruction pointer, and thus program flow, to the address you give. JMP mem_ref will execute the code at mem_ref, while JMP [mem_ref] will jump to the code that mem_ref points to.
CALL value - Same as JMP, but pushes the address of the instruction after the CALL.
RETN - Opposite of CALL: pops an address off the stack and jumps to it.
RETN xx - Like RETN, but adds xx to ESP (stack pointer) after returning. Useful for stdcall functions that take arguments.
Now, conditional instructions...
The conditions are: (Prefix with N to negate it - NE [not equal] is the opposite of E [equal])
Z, E = Zero, Equal
G, NLE = Greater than (signed)
L, NGE = Less than (signed)
A, NBE = Greater than (unsigned)
B, NAE = Less than (unsigned)
GE, NL = Greater than or equal to (signed)
LE, NG = Less than or equal to (signed)
AE, NB = Greater than or equal to (unsigned)
BE, NA = Less than or equal to (unsigned)
C = Carry flag is set
P, PO, NPE = Parity is odd
NP, NPO, PE = Parity is even
ECXZ = ECX is 0
CXZ = CX is 0
O = Integer overflow occured
S = Sign bit is set (... I think - not entirely sure as I've never used it)
You can use J[condition] as a conditional jump, like this:
MOV ECX,20
label:
DEC ECX
JNZ label ; jump if not zero. There is no JNECXZ, by the way.
The C equivalent of this is:
for(i=20;i;i--);
One rather special instruction that you may need for "Logical" NOT, and other logical instructions, is SET[condition] - like so:
test EAX,EAX
setnz AL
movzx EAX,AL
(Logical NOT of EAX)
SET[condition] sets a single-byte value (register or memory) to 1 if the condition is satisfied, otherwise 0.
Since I am not really a natural-born teacher, I wonder if I skipped anything. Either way, the next instructions you might like to learn would be string instructions:
REP STOSB - (if ECX is larger than 0) Stores AL into BYTE[EDI], increases EDI, decreases ECX, and loops until ECX is 0.
REPNE SCASB - (if ECX is larger than 0) Compares AL with BYTE[ESI], decreases ECX, increases ESI, and loops if ECX>0 and BYTE[ESI before we increased it]!=0
You can use those instructions without REP (or repne, etc), which removes looping and all references to ECX.
There are many more string instructions.
Another very useful instruction is LEA. It pretty much returns the address you used instead of the memory at that address. It is useful because it means you can perform quick arithmetic and move it into another register:
LEA EBX, [EAX+ECX*4+mem_location]
(C-style: ebx = eax + ecx*4 + &mem_location; )
Note that the address doesn't have to be valid:
XOR EAX,EAX
LEA EAX, [EAX*2+1]
This is valid, even though referencing that address would most likely cause a segfault.
If you need an instruction reference:
http://www.rz.uni-karlsruhe.de/rz/docs/VTune/reference/About_IA-32_Instructions.htmThough I'm pretty sure I missed... everything... those are the instructions you'll want to know.
(I definitely missed FPU instructions...)
I also forgot to mention that to return a string, you MOV EAX to equal a pointer to the string (and make sure the FPU stack is in the same position as it was when you started - but when you return a double, you have to have 1 more item on the FPU stack than when you started).