One of the advantages of assembly language programming is that often you can completely eliminate the need for memory variables. You just keep as much of the data in the processor registers as you can.

This can have a serious drawback on the understanding of your own code when you read it after a while.

To solve the problem, I always think of registers as variables, and assign them "names". I don't go so far as to use macros to rename the registers, but on every line of code that I change the value of a register, I place a comment in which I treat that register as a variable.

I have developed a style in which a comment can be read as the continuation of the previous comment: While I code in lower case letters, I use caps for the registers in the comments. That way I do not have to repeat the register name on each comment line because I can easily identify what register the comment is referring to by seeing the closest line that contains capital letters.

To give you a simplistic example of how I do it, I always start by writing some kind of pseudocode first. So, if I wanted to write a function that multiplies two variables and adds 10 to them, this is what the code would look like (in NASM syntax):

    ; a = a * b + 10
    ; [esp+4] = a
    ; [esp+8] = b

    mov    eax, [esp+4]         ; EAX = a
    mul    eax, dword [esp+8]   ;  * b
    add    eax, 10              ;  + 10

This may sound like too much work. But it is a habit and has become completely automatic. I don't even have to think about, I just do it.

Whenever I have looked at my code after a while, I have always been glad to find those comments. Indeed, it has made debugging much easier. As soon as I finish writing a routine, I go through it and look more at the comments than the code. That way I can make sure the program flows the way I intended it to. It also makes it easier to find ways of optimizing the code even further.