Assembly programming advices and ideas

Programming, for all ages and all languages.
Post Reply
Antti
Member
Member
Posts: 923
Joined: Thu Jul 05, 2012 5:12 am
Location: Finland

Assembly programming advices and ideas

Post by Antti »

A little rambling topic in between important topics. Let see whether it takes off or not. I have been programming in assembly for some time now and I would like to list some rather obvious advices. This is not a comprehensive list but just a few random thoughts.
  • Usage. Do not write in assembly unless you have a good reason to do so, e.g. a hobby, tool-chain related issues, etc.
  • Modularization. Keep code units rather small. This does not necessarily mean that you need to split procedures into subprocedures but modern assemblers are good at "including" files so build your programs by "including" small units into bigger units. Do not go too far, i.e. avoid long dependency chains.
  • Registers. As far as I know, this is one of the biggest problems. Be absolutely sure which registers are preserved and which are not. It may be convenient to have some kind of "dependency barriers" along the way. For example, "from this point on I know the exact state of the registers and any modifications before this point do not have any effect." It is a form of art to do this with minimum overhead.
  • Calling convention. Related to the previous points. Have a strict calling convention for "public interfaces". Although debatable, I recommend having the "callee saves all but return values" approach. Internal procedures can be rather liberal and that is one of the advantages of assembly programming.
  • Meta programming. Use macros or other helper tools if creating long and repetitive code sections. Usually much less error-prone.
  • Macros, defines, etc. A contradictory topic. Useful if done right. Extremely bad if done wrong. It is easy to vefify the code if you know exactly what it does.
  • Comments. Rules are simple: use comments almost everywhere.
Please add your comments and ideas.
User avatar
max
Member
Member
Posts: 616
Joined: Mon Mar 05, 2012 11:23 am
Libera.chat IRC: maxdev
Location: Germany
Contact:

Re: Assembly programming advices and ideas

Post by max »

Aggree to all of these, except:
  • Calling convention. Have a strict calling convention for "public interfaces". Callee saves only registers that it modifies, except the ones used for return.
I think saving all is overkill. You keep track of what registers you modify. I disagree on the internal procedures part; they should follow the same convention, otherwise they could trash stuff. This depends on how you define internal; if public procedure A and public procedure B both might use it, the problems begin.
Antti
Member
Member
Posts: 923
Joined: Thu Jul 05, 2012 5:12 am
Location: Finland

Re: Assembly programming advices and ideas

Post by Antti »

max wrote:I disagree on the internal procedures part; they should follow the same convention, otherwise they could trash stuff.
Internal procedures could be like "static functions" in C. It is much more flexible to have loose rules for them.
max wrote:if public procedure A and public procedure B both might use it, the problems begin.
If those procedures, both A and B, are in the same module, it is possible that both of them use the same internal procedure.
glauxosdever
Member
Member
Posts: 501
Joined: Wed Jun 17, 2015 9:40 am
Libera.chat IRC: glauxosdever
Location: Athens, Greece

Re: Assembly programming advices and ideas

Post by glauxosdever »

Hi,

Programming in assembly can be entertaining and you might gain something from it, but it can also be sometimes very frustrating. I think this topic isn't really that rambling, but rather useful for everyone programming in assembly.

I agree modules should be small. This way they are more maintainable.

Now, about registers: As Antti said, always make sure which registers are preserved and which are restored. To make sure, push all used registers at the start of the call and restore them when done, so you can remain quiet that they were not altered by the call. The same goes with variables too.

I prefer not using macros, because they add executable code without noticing it. Use calls wherever possible, except you want to optimize your code for speed. Constants are fine.

Always document your code. At each call, document input and output values so you pass these that are needed for the call. Describe carefully what is the function of each register inside each call. Put local labels every 5-10 lines inside each call, that describe what's going on in the next 5-10 lines, even if there are no jumps to them. Comment every single line of code.

Try to write with least lines of code possible. Assembly is generally hard to read (optimized or not), so don't make it even harder by writing lengthy code.

I hope this was useful advise.

Regards,
glauxosdever
Antti
Member
Member
Posts: 923
Joined: Thu Jul 05, 2012 5:12 am
Location: Finland

Re: Assembly programming advices and ideas

Post by Antti »

glauxosdever wrote:Try to write with least lines of code possible.
It is not always good. For example, sometimes unrolling a short loop can make code much more readable.
glauxosdever
Member
Member
Posts: 501
Joined: Wed Jun 17, 2015 9:40 am
Libera.chat IRC: glauxosdever
Location: Athens, Greece

Re: Assembly programming advices and ideas

Post by glauxosdever »

Hi,

Antti: Generally, writing less code is most times better, because code that doesn't exist has no bugs and you can maintain it more easily. There are always cases where writing more lines can make code more readable, but these are less usual than the other cases.

You can save lines by avoiding unnecessary CMP instructions. For example a CMP ECX, 0 is pointless after a DEC ECX because DEC instruction will set the zero flag if ECX equals 0.

You can also use registers instead of main memory to store values. Moving a value from memory to memory can't be done in one single instruction, instead a register will be needed between. Writing to registers is faster compared to writing to memory, so for loops that are executed several thousands of times per second, it is really better to use registers for storage; the speedup will be noticeable.

Also, if you want to push general purpose registers all-in-one, you can use the PUSHA/PUSHAD instruction, and to pop them all-in-one, you can use the POPA/POPAD instruction.

There are also other size optimization tricks that I'm not aware now. They could make code not only more readable, but also faster.

Regards,
glauxosdever
User avatar
iansjack
Member
Member
Posts: 4685
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Assembly programming advices and ideas

Post by iansjack »

You can save lines by avoiding unnecessary CMP instructions. For example a CMP ECX, 0 is pointless after a DEC ECX because DEC instruction will set the zero flag if ECX equals 0.
Although, in practice, the DEC ECX is probably unnecessary; more likely you would just use a LOOP.
Also, if you want to push general purpose registers all-in-one, you can use the PUSHA/PUSHAD instruction, and to pop them all-in-one, you can use the POPA/POPAD instruction.
I would say that it is probably better to PUSH/POP registers individually; you rarely need to save all of them. In any case, the PUSHA/POPA instructions are unavailable in 64-bit mode and I would imagine that those looking to the future will be looking at 64-bit code.
Antti
Member
Member
Posts: 923
Joined: Thu Jul 05, 2012 5:12 am
Location: Finland

Re: Assembly programming advices and ideas

Post by Antti »

glauxosdever wrote:instead a register will be needed between
There is one trick trick if, for some reason, you want to preserve registers.

Code: Select all

push dword [SOURCE]
pop dword [DESTINATION]
It is good that you brought up the FLAGS register. It is extremely important to know which instructions change flags and which not. For example, the LEA instruction is good if you want to make some simple calculations without changing the FLAGS register.
User avatar
TightCoderEx
Member
Member
Posts: 90
Joined: Sun Jan 13, 2013 6:24 pm
Location: Grande Prairie AB

Re: Assembly programming advices and ideas

Post by TightCoderEx »

USAGE:
My personal preference is, when I write this;

Code: Select all

    @@: mov     ah, [edi - 1]
        mov     al, ' '
        push    edi
        rep     stosw
        mov      cl,  dl
        inc      ch
        pop      edi

        pop     esi
I see this disassembly

Code: Select all

00000040  8A67FF            mov ah,[edi-0x1]
00000043  B020              mov al,0x20
00000045  57                push edi
00000046  F366AB            rep stosw
00000049  88D1              mov cl,dl
0000004B  FEC5              inc ch
0000004D  5F                pop edi
0000004E  5E                pop esi
So there is a one to one correlation between source and object. WYSIWYG in essence. As my project is focused strictly on X86 and BIOS, tools like FASM & NASM are sufficient and my project is not dependant file systems or shared libraries.

MODULARIZATION:
I don't concern myself too much with size, but keep subject matter contained. As an example, currently I'm working on a comprehensive algo to display text on an 80 x 25 (16 color) display that uses characters (1 - 31) for special functions and formatting in one file. Then there will be another for conversion routines Binary -> ASCIIZ and vise versa.

REGISTERS
As I've done quite a bit of Win32 programming, I've adopted M$ preserve everything except EAX, ECX & EDX. This is predicated entirely on specifics of routine, but it is a general rule of thumb I use. Because of this, I've also adopted CDECL calling convention. LEAVE or MOV ESP, EBP at epilogue is a convenient means by which to make sure stack doesn't get out of control.

I never use macros or any higher level constructs, but amply document my code although previous example doesn't show it.
Antti
Member
Member
Posts: 923
Joined: Thu Jul 05, 2012 5:12 am
Location: Finland

Re: Assembly programming advices and ideas

Post by Antti »

glauxosdever wrote:Also, if you want to push general purpose registers all-in-one, you can use the PUSHA/PUSHAD instruction, and to pop them all-in-one, you can use the POPA/POPAD instruction.
Although this is compact in size, I am not sure whether it is actually faster or not. The real problem comes when your procedures have return values. Usually there are not that much registers you need to push/pop, so single pushes and pops may be better. On x86-64 that is the only option.
Post Reply